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 NameData TypeDescription

    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

RoundDelay(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 updated