PNS (Payment Notification Service)

If the app that uses ONE store's IAP V5 initiates payment or payment cancellation transactions through the ONE store payment system, ONE store passes a notification to the pre-configured Developer Center Server. The PNS (Payment Notification Service) can be used as a means to check the loss of payment information that may occur on mobile, and makes a push notification on payment and payment cancellation, respectively.

  • Since the payment notification may also be lost or the server that sends and receives notifications may fail, developers must not use the push notification they receive as a ground for providing in-app products.

  • The developers are required to provide the in-app products based on the payment completion response which is made for the payment request of the app. The payment notification is used for customer service purposes, including payment confirmation and withdrawal of items.

  • If you want to check the payment with a server-to-server, do not use PNS notification and we recommend using related server APIs like getPurchaseDetail or getPurchaseDetailByProduct.

  • ONE store can perform a payment test for verification and monitoring purposes, and the test results will be sent the same notification upon payment/cancellation. The payment test details are periodically cancelled by ONE store.

  • You can set up the developer's service server which receives the payment notification by selecting an in-app product on the 'Developer Center > Apps > My Applications' page and clicking the 'Payment Notification' button on the 'In-App' menu.

  • Payment Notification Message Format (ONE store → the developer's service server)

  • Payment Notification Message Sample

{
  "msgVersion": "2.0.0.D",
  "packageName":"com.onestore.pns",
  "productId":"0900001234",
  "messageType":"SINGLE_PAYMENT_TRANSACTION",
  "purchaseId":"SANDBOX3000000004564",
  "developerPayload":"OS_000211234",
  "purchaseTimeMillis":24431212233,
  "purchaseState":"COMPLETED",
  "price":10000,
  "productName":"GOLD100(+20)"
  "paymentTypeList":[
    {
      "paymentMethod":"DCB",
      "amount":3000
    },
    {
      "paymentMethod":"ONESTORECASH",
      "amount":7000
    }
  ],
  "billingKey" : "36FED4C6E4AC9E29ADAF356057DB98B5CB92126B1D52E8757701E3A261AF49CCFBFC49F5FEF6E277A7A10E9076B523D839E9D84CE9225498155C5065529E22F5",
  "isTestMdn" : true,
  "signature":   "BwJdUVT/iFFT1MKIFZTdkD/y5+b8h4hCuVB3zVrYcT7pMf1wuWrXNZK9ZA1FhUlWPa7C10Do4CDr8k28QOejGOCgiit5RYzL1tF5eRjFkY66oD3qfNvexkt5wwVjJP5EYyzqwCDVkbx004eGUX46LzaxVV7i137e4KyUrdk9Q5c="
}
  • paymentMethod of ONE store

  • How to check the validity of the signature

    • Using the codes below, you can verify that the signature is valid.

    • The public key in the source code means the License key provided by ONE store development center(Apps > In-App > Credentials).

    • For more information, refer to the 'Issue License Key' in the Preparatory Work page.

JAVA

import java.security.PublicKey;
    
import org.apache.commons.codec.binary.Base64;
import org.codehaus.jackson.JsonNode;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.node.ObjectNode;
    
    
public class SignatureVerifier {
    
    private static final String SIGN_ALGORITHM = "SHA512withRSA";
    private ObjectMapper mapper = new ObjectMapper();
    
    
    boolean verify(String rawMsg, PublicKey key) throws Exception {
        // get the signature from the JSON message
        JsonNode root = mapper.readTree(rawMsg);
        String signature = root.get("signature").getValueAsText();
        ((ObjectNode)root).remove("signature");
           
        // check the validity of the signature
        Signature sign = Signature.getInstance(SIGN_ALGORITHM);
        sign.initVerify(key);
        sign.update(root.toString().getBytes("UTF-8"));
        return sign.verify(Base64.decodeBase64(signature));
    }
}

PHP

<?php
function formatPublicKey($publicKey) {
    $BEGIN= "-----BEGIN PUBLIC KEY-----";
    $END = "-----END PUBLIC KEY-----";
   
    $pem = $BEGIN . "\n";
    $pem .= chunk_split($publicKey, 64, "\n");
    $pem .= $END . "\n";
   
    return $pem;
}
   
function formatSignature($signature) {
    return base64_decode(chunk_split($signature, 64, "\n"));
}
   
// Sample message
$sampleMessage = '{"msgVersion":"2.0.0.D","purchaseId":"SANDBOX3000000004564","developerPayload":"OS_000211234","packageName":"com.onestore.pns","productId":"0900001234","messageType":"SINGLE_PAYMENT_TRANSACTION","purchaseMillis":24431212233,"purchaseState":"COMPLETED","price":20000,"productName":"한글은?GOLD100(+20)","paymentTypeList":[{"paymentMethod":"DCB","amount":3000},{"paymentMethod":"ONESTORECASH","amount":7000}],"billingKey":"36FED4C6E4AC9E29ADAF356057DB98B5CB92126B1D52E8757701E3A261AF49CCFBFC49F5FEF6E277A7A10E9076B523D839E9D84CE9225498155C5065529E22F5","isTestMdn":true,"signature":"MNxIl32ws+yYWpUr7om+jail4UQxBUXdNX5yw5PJKlqW2lurfvhiqF0p4XWa+fmyV6+Ot63w763Gnx2+7Zp2Wgl73TWru5kksBjqVJ3XqyjUHDDaF80aq0KvoQdLAHfKze34cJXKR/Qu8dPHK65PDH/Vu6MvPVRB8TvCJpkQrqg="}';
   
// Sample public key
$publicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDMzpWJoK1GSOrr4juma5+sREYjdCW8/xSd9+6z6PAkUH5af97wy8ecfkLtP9LK5VskryfDlcOjfu0BgmHYntAqKT7B4KWk8jWbJ8VHUpp30H95UbcnCRFDqpEtwYzNA5gNMYKtAdbL41K8Fbum0Xqxo65pPEI4UC3MAG96O7X1WQIDAQAB";
   
   
// Parse JSON message
$jsonArr = json_decode($sampleMessage, true);
   
// Extract and remove signature
$signature = $jsonArr["signature"];
unset($jsonArr["signature"]);
$originalMessage = json_encode($jsonArr, JSON_UNESCAPED_UNICODE);
   
// Veify
$formattedKey = formatPublicKey($publicKey);
$formattedSign = formatSignature($signature);
$hash_algorithm = 'sha512';
   
$success = openssl_verify($originalMessage, $formattedSign, $formattedKey, $hash_algorithm);
if ($success == 1) {
    echo "verified";
}
else {
    echo "unverified";
}
?>

Python

# -*- coding: utf-8 -*-
   
import json
from base64 import b64decode
from collections import OrderedDict
   
from Crypto.Hash import SHA512
from Crypto.PublicKey import RSA
from Crypto.Signature import PKCS1_v1_5
   
import sys
reload(sys)
sys.setdefaultencoding('utf-8')
   
hash = "SHA-512"
   
   
def verify(message, signature, pub_key):
    signer = PKCS1_v1_5.new(pub_key)
    digest = SHA512.new()
    digest.update(message)
    return signer.verify(digest, signature)
   
   
jsonData = json.loads(rawMsg, encoding='utf-8', object_pairs_hook=OrderedDict)
signature = jsonData['signature']
del jsonData['signature']
originalMessage = json.dumps(jsonData, ensure_ascii=False, encoding='utf-8', separators=(',', ':'))
   
RSA.importKey(publickey).publickey()
print(verify(originalMessage, b64decode(signature), RSA.importKey(publickey).publickey()))

Last updated