English
Search
K

Use PNS (payment notification service)

Overview

If the transaction of in-app product billing or billing cancellation occurs, ONE store provides PNS (payment notification service), which sends notification to the developer’s server.
The notification might be delayed or lost depending on the status of transmitting/receiving servers, and therefore you must not provide in-app(services) based on the reception of the notification.
You must provide the in-app based on the purchase completion response (or the completion of the ‘consumed’ process), and it is recommended to use notification for the purpose of confirming the purchase or retrieving items after the purchase is cancelled.
If you want to identify whether the purchase is valid through server-to-server, it is recommended to search with the related server API (getPurchaseDetails) instead of using PNS notification.
ONE store can perform the billing test for the purpose of reviewing and monitoring, and in addition, the notification will be sent when the billing/billing cancellation is performed in the billing test. The history of the billing test performed by ONE store will be periodically cancelled by ONE store.

Set Up PNS Receiving Server URL

You can set the URL of the developer’s server to receive PNS by clicking on the ‘Manage PNS’ button on the ‘Developer Center > Apps > Select In-Apps > In-App’ menu.
The URL can set up the Sandbox (for development) and commercial (including commercial test) test environments respectively, and if the development/commercial servers are the same, enter the same URL.

PNS Details

Specifications for sending PNS message (ONE store → developer server)

  • URI : It is the Notification URL set up by Developer Center.
  • Method : POST
  • Request Parameters : N/A
  • Request Header :
    Parameter Name
    Data Type
    Description
    Content-Type
    String
    application/json
  • Request Body : JSON format
Element Name
Data Type
Description
msgVersion
String
Message version
  • Development (Sandbox): 3.0.0D
  • Commercial (commercial test): 3.0.0
packageName
String
Package name of the app
productId
String
In-app ID
messageType
String
Fix SINGLE_PAYMENT_TRANSACTION
purchaseId
String
Purchase ID
developerPayload
String
Identifier managed by the developer to identify purchases
purchaseTimeMillis
Long
Time (ms) when the billing is completed in ONE store billing system
purcahseState
String
COMPLETED: billing completed / CANCELED: cancelled
price
String
Payment amount
priceCurrencyCode
String
Currency code for payment amount (KRW, USD, ...)
productName
String
It is transmitted when the developer sets up the customized in-app title at the time of purchase request.
paymentTypeList
List
List of payment information
paymentMethod
String
Payment method (for details, refer to the definition of paymentMethod)
amount
String
Amount per payment method
billingKey
String
Billing key for extended function
isTestMdn
Boolean
Test phone or not (true: test phone, false: not test phone)
purchaseToken
String
Purchase token
environment
String
Test environment
  • Development (Sandbox): SANDBOX
  • Commercial:COMMERCIAL
marketCode
String
Market identification code (MKT_ONE: ONE store, MKT_STM: Storm+ )
signature
String
Signature for this message
Example
{
"msgVersion" : "3.0.0"
"packageName":"com.onestore.pns",
"productId":"0900001234",
"messageType":"SINGLE_PAYMENT_TRANSACTION",
"purchaseId":"SANDBOX3000000004564",
"developerPayload":"OS_000211234",
"purchaseTimeMillis":24431212233,
"purchaseState":"COMPLETED",
"price":"10000",
"priceCurrencyCode":"KRW"
"productName":"GOLD100(+20)"
"paymentTypeList":[
{
"paymentMethod":"DCB",
"amount":"3000"
},
{
"paymentMethod":"ONESTORECASH",
"amount":"7000"
}
],
"billingKey" : "36FED4C6E4AC9E29ADAF356057DB98B5CB92126B1D52E8757701E3A261AF49CCFBFC49F5FEF6E277A7A10E9076B523D839E9D84CE9225498155C5065529E22F5",
"isTestMdn" : true,
"purchaseToken" : "TOKEN...",
"environment" : "SANDBOX",
"marketCode" : "MKT_ONE"
"signature" "SIGNATURE..."
}

Definition of paymentMethod (ONE store payment method)

paymentMethod
Payment method name
Description
DCB
Mobile phone payment
It is charged as the ‘information use fee’ item in the telecommunication bill by mobile carriers
PHONEBILL
Mobile phone micropayment
It is charged as the ‘micropayment’ item in the telecommunication bill by mobile carriers.
ONEPAY
ONE pay
Simple credit card payment provided by ONE store
ONEPAYBANKACCT
ONE pay account payment
Simple account payment provided by ONE store
ONEPAYDCB
ONE pay mobile phone payment
Simple mobile phone payment provided by ONE store
ONEPAYPHONEBILL
ONE pay mobile phone micropayment
Simple micropayment provided by ONE store
CREDITCARD
Credit card
Typical credit card payment
11PAY
11Pay
Simple mobile phone payment provided by SK planet
NAVERPAY
N pay
Naver pay’s payment provided by Naver
CULTURELAND
Culture cash
Culture cash payment provided by Korea Culture Promotion Inc.
TMEMBERSHIP
T membership
T membership payment provided by SK telecom
OCB
OK cashbag
OK cashback payment provided by SK planet
GAMECASH
Game cash
ONE store game cash payment
ONESTORECASH
ONE store cash
ONE store cash payment
ONESTORECOUPON
ONE store coupon
ONE store coupon payment
TMONEY
Mobile T Money
Mobile T Money payment provided by T Money
KTMEMBERSHIP
KT membership
KT membership payment
LGMEMBERSHIP
U+ membership
LG U+ membership payment
PAYCO
ONE pay Payco
'Payco' payment provided by ONE store
MYACCT
ONE pay Myaccout
'Myaccout' payment provided by ONE store
IAACOMMON
ONE store Point (Common)
ONE store point that applies in common
IAAGAME
ONE store Point (Individual)
ONE store point that applies only to certain games/apps

How to review signature

You can check whether signature is forged or falsified by using the code below.
  • PublicKey within the code indicates the license key provided on ‘Developer Center > In-App > Credentials’. For details on the license key, refer to ‘Check License Key (public key) & OAuth Credentials’ within the Pre-preparations 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 {
// Extract signature from JSON message
JsonNode root = mapper.readTree(rawMsg);
String signature = root.get("signature").getValueAsText();
((ObjectNode)root).remove("signature");
// Review whether the extracted signature is the valid value or not.
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()))

Notification transmission policy

ONE store’s PNS server shall send notification to the developer’s server through HTTP(S) request.
At this time, the developer’s server must respond with the HTTP Status Code as 200 to indicate that the notification has been normally received. If the HTTP Status Code fails to be received as 200 as the response due to the loss of notification caused by delays in the network or due to the
failure in the developer’s server, the PNS server determines that the notification transmission has failed, and thereby will perform up to 30 rounds of retransmission during 3 days.
The retransmission of the notification will be performed after certain delays as seen in the example below, and more retransmission rounds will lead to more delays.
Example
Round
Delay(s)
Retransmission time
0 (first)
0
2020-05-17 13:10:00
1
30
2020-05-17 13:10:30
2
120
2020-05-17 13:12:30
3
270
2020-05-17 13:17:00
4
480
2020-05-17 13:25:00
...
...
...
Last modified 11mo ago