# Using PNS (Push Notification Service)

## Overview <a href="#usingpns-pushnotificationservice-overview" id="usingpns-pushnotificationservice-overview"></a>

ONE store provides two Push Notification Services for developers.

* If the transaction of in-app product billing or billing cancellation occurs, ONE store provides PNS (payment notification service), which sends a notification to the developer's server.
* An SNS (Subscription Notification Service) that sends notifications to the developer server when the subscription status changes

{% hint style="warning" %}

* The notification might be delayed or lost depending on the status of transmitting/receiving servers, and therefore it is not recommended that you provide in-app (services) based on the reception of the notification.
* If you want to identify whether the purchase is valid through server-to-server, it is recommended to search with the related server API instead of using PNS notifications.
* 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 canceled by ONE store.
  {% endhint %}

## Set Up PNS Receiving Server URL <a href="#usingpns-pushnotificationservice-setuppnsreceivingserverurl" id="usingpns-pushnotificationservice-setuppnsreceivingserverurl"></a>

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  <a href="#usingpns-pushnotificationservice-pnsdetails" id="usingpns-pushnotificationservice-pnsdetails"></a>

{% hint style="info" %}
Due to the Developer Center update on March 20, 2025, PNS version 3.1.0 has been added.

* The `packageName` parameter has been changed to `clientId`, and there are no changes for versions 3.0.0 and below.
  {% endhint %}

### Specifications for Sending Payment Notification Messages (ONE store → Developer Server) <a href="#usingpns-pushnotificationservice-specificationsforsendingpaymentnotificationmessages-onestore-develo" id="usingpns-pushnotificationservice-specificationsforsendingpaymentnotificationmessages-onestore-develo"></a>

* **URI**: Payment Notification URL set up by the Developer Center
* **Method**: POST
* **Request Parameters**: N/A
* **Request Header**:&#x20;

  | **Parameter Name** | **Data Type** | **Description**  |
  | ------------------ | ------------- | ---------------- |
  | Content-Type       | String        | application/json |
* **Request Body**: JSON format

  | **Element Name**              | **Data Type** | **Description**                                                                                                   |
  | ----------------------------- | ------------- | ----------------------------------------------------------------------------------------------------------------- |
  | <p>msgVersion</p><p><br></p>  | String        | <p>Message version</p><ul><li>Development (Sandbox): 3.1.0D</li><li>Commercial (commercial test): 3.1.0</li></ul> |
  | <p>clientId</p><p><br></p>    | String        | Client ID of the app                                                                                              |
  | <p>productId</p><p><br></p>   | String        | In-app ID                                                                                                         |
  | <p>messageType</p><p><br></p> | 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                                               |
  | purchaseState                 | String        | <p>COMPLETED: billing completed </p><p>CANCELED: canceled</p>                                                     |
  | 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                                                                                       |
  | <p><br>paymentMethod</p>      | String        | Payment method (for details, refer to the definition of paymentMethod)                                            |
  | <p>amount<br></p>             | 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        | <p>Test environment</p><ul><li>Development (Sandbox): SANDBOX</li><li>Commercial:COMMERCIAL</li></ul>             |
  | marketCode                    | String        | Market identification code (MKT\_ONE: ONE store, MKT\_GLB: Global ONE store)                                      |
  | signature                     | String        | Signature for this message                                                                                        |
* **Example**

```
{
	"msgVersion" : "3.1.0"
	"clientId":"0000000001",
	"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) <a href="#usingpns-pushnotificationservice-definitionofpaymentmethod-onestorepaymentmethod" id="usingpns-pushnotificationservice-definitionofpaymentmethod-onestorepaymentmethod"></a>

| paymentMethod   | Payment method name       | Description                                                                                |
| --------------- | ------------------------- | ------------------------------------------------------------------------------------------ |
| DCB             | Mobile phone payment      | Charged as the 'information use fee' item in the telecommunication bill by mobile carriers |
| PHONEBILL       | Mobile phone micropayment | Charged as the 'micropayment' item in the telecommunication bill by mobile carriers        |
| ONEPAY          | ONE pay                   | Easy pay 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.                              |
| TELCOMEMBERSHIP | Mobile carrier membership | Membership payment provided by the mobile carrier                                          |
| OCB             | OK cashbag                | OK cashback payment provided by SK planet                                                  |
| ONESTORECASH    | ONE store cash            | ONE store cash payment                                                                     |
| COUPON          | ONE store coupon          | ONE store coupon payment                                                                   |
| EWALLET         | e-Wallet                  | e-Wallet Payment                                                                           |
| BANKACCT        | Account Payment           | Regular Account Payment                                                                    |
| PAYPAL          | Paypal                    | Payment provided by PayPal                                                                 |
| MYCARD          | My Card                   | Payment with MyCard provided by Soft World                                                 |

#### How to review the signature <a href="#usingpns-pushnotificationservice-howtoreviewthesignature" id="usingpns-pushnotificationservice-howtoreviewthesignature"></a>

You can check whether the signature has been forced or falsified by using the code below.

* The PublicKey in the code refers to the license key provided in the "License Management" menu.

{% tabs %}
{% tab title="JAVA" %}

```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 {
        // JSON 메시지에서 signature를 추출한다.
        JsonNode root = mapper.readTree(rawMsg);
        String signature = root.get("signature").getValueAsText();
        ((ObjectNode)root).remove("signature");
          
        // 추출한 signature가 올바른 값인지 검증한다.
        Signature sign = Signature.getInstance(SIGN_ALGORITHM);
        sign.initVerify(key);
        sign.update(root.toString().getBytes("UTF-8"));
        return sign.verify(Base64.decodeBase64(signature));
    }
}
```

{% endtab %}

{% tab title="PHP" %}

```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";
}
?>
```

{% endtab %}

{% tab title="Python" %}
{% code overflow="wrap" %}

```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()))
```

{% endcode %}
{% endtab %}
{% endtabs %}

### Specifications for Sending Subscription Notification Messages (ONE store → Developer Server) <a href="#usingpns-pushnotificationservice-specificationsforsendingsubscriptionnotificationmessages-onestore-d" id="usingpns-pushnotificationservice-specificationsforsendingsubscriptionnotificationmessages-onestore-d"></a>

* **URI**: Subscription Notification URL set up by the Developer Center
* **Method**: POST
* **Request Parameters**: N/A
* **Request Header**:&#x20;

  | **Parameter Name** | **Data Type** | **Description**  |
  | ------------------ | ------------- | ---------------- |
  | Content-Type       | String        | application/json |
* **Request Body**: JSON format

  | **Element Name**         | **Data Type**    | **Description**                                                                                         |                                       |   |
  | ------------------------ | ---------------- | ------------------------------------------------------------------------------------------------------- | ------------------------------------- | - |
  | msgVersion               | String           | <p>Message version</p><ul><li>Development (Sandbox): 3.1.0D</li><li>Commercial (commercial test): 3.1.0 | </li></ul>                            |   |
  | clientId                 | String           | Client ID of the app                                                                                    |                                       |   |
  | eventTimeMillis          | Long             | Time when event occurs                                                                                  |                                       |   |
  | subscriptionNotification | Object           | List of payment information                                                                             |                                       |   |
  | <p><br></p>              | version          | String                                                                                                  | Version of subscription alarm message |   |
  | <p><br></p>              | notificationType | Integer                                                                                                 | Subscription status                   |   |
  | <p><br></p>              | purchaseToken    | String                                                                                                  | Purchase token                        |   |
  | <p><br></p>              | productId        | String                                                                                                  | In-app ID                             |   |
  | environment              | String           | <p>Test environment</p><ul><li>Development (Sandbox): SANDBOX</li><li>Commercial:COMMERCIAL             | </li></ul>                            |   |
  | marketCode               | String           | Market identification code (MKT\_ONE: ONE store, MKT\_STM: Storm)                                       |                                       |   |
* **Example**

```json
{
    "msgVersion":"3.1.0",
    "clientId":"0000000001",
    "eventTimeMillis":24431212233000,
    "subscriptionNotification": {
        "version": "1",
        "notificationType" : 1,
        "purchaseToken":"TOKEN",
        "productId": "com.product.id"
    },
    "environmenmt": "COMMERCIAL",
    "marketCode": "MKT_ONE"
}
```

#### Defining Subscription Statuses <a href="#usingpns-pushnotificationservice-definingsubscriptionstatuses" id="usingpns-pushnotificationservice-definingsubscriptionstatuses"></a>

| **Subscription status** | **Subscription Code**                  | **Description**                                                            |
| ----------------------- | -------------------------------------- | -------------------------------------------------------------------------- |
| 1                       | SUBSCRIPTION\_RECOVERED                | The subscription has been recovered from its pending status.               |
| 2                       | SUBSCRIPTION\_RENEWED                  | The subscription has been renewed.                                         |
| 3                       | SUBSCRIPTION\_CANCELED                 | The user has requested to cancel the subscription.                         |
| 4                       | SUBSCRIPTION\_PURCHASED                | A new subscription has been purchased.                                     |
| 5                       | SUBSCRIPTION\_ON\_HOLD                 | The subscription has been put on hold due to payment failure.              |
| 6                       | SUBSCRIPTION\_IN\_GRACE\_PERIOD        | The subscription has been put on a deferred status due to payment failure. |
| 7                       | SUBSCRIPTION\_RESTARTED                | The user has canceled the request to cancel the subscription.              |
| 8                       | SUBSCRIPTION\_PRICE\_CHANGE\_CONFIRMED | The user has agreed to the changes in the subscription price.              |
| 9                       | SUBSCRIPTION\_DEFERRED                 | The subscription period has been deferred.                                 |
| 10                      | SUBSCRIPTION\_PAUSED                   | The subscription has been paused.                                          |
| 11                      | SUBSCRIPTION\_PAUSE\_SCHEDULE\_CHANGED | The "pause schedule" of the subscription has been changed.                 |
| 12                      | SUBSCRIPTION\_REVOKED                  | The subscription has been terminated.                                      |
| 13                      | SUBSCRIPTION\_EXPIRED                  | The subscription has expired.                                              |

<br>

#### Notification Transmission Policy <a href="#usingpns-pushnotificationservice-notificationtransmissionpolicy" id="usingpns-pushnotificationservice-notificationtransmissionpolicy"></a>

ONE store's PNS server shall send notification to the developer's server through HTTP(S) requests.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.

<br>

**Example**

| **Round** | **Delay(seconds)** | **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     |
| ...       | ...                | ...                     |
