한국어
Search
K

使用PNS(Push Notification Service)

概要

ONE store为开发者提供两种Push Notification Service。
  • 第一种是PNS(Payment Notification Service,支付通知服务),当发生In-App商品支付或取消支付时,ONE store向开发公司服务器发送通知。
  • 第二种是SNS(Subscription Notifacation Service,订阅通知服务),在订阅状态发生变化时向开发公司服务器发送通知。
注意
由于notification可能因发送/接收服务器的状态而延迟或丢失,因此不建议以接收notification为准提供App商品(服务)。
如果您想以服务器到服务器方式确认是否正常付款,我们建议您使用相关服务器API进行查询,而不是使用PNS notification。
ONE store可能会进行付款测试以便验证和监控,在付款/取消付款时,这些测试也会收到同样的notification。ONE store的付款测试历史记录会定期由ONE store自行取消。

设置PNS接收服务器URL

您可以通过点击"开发者中心>Apps>App商品选择>In-App信息"菜单中的"管理PNS"按钮来设置接收PNS的开发公司服务器的URL。
URL可以分别设置Sandbox(开发用)支付环境和商用(包括商用测试)支付环境,如果开发用/商用服务器相同,则输入相同的URL即可。

PNS详细信息

Payment Notification消息发送规格(ONE store→开发公司服务器)

  • URI : 在开发者中心设置的 Payment Notification URL
  • Method : POST
  • Request Parameters : N/A
  • Request Header
    Parameter Name
    Data Type
    Description
    Content-Type
    String
    application/json
  • Request Body : JSON格式
    Element Name
    Data Type
    Description
    msgVersion
    String
    消息版本
    • 开发(Sandbox) : 3.0.0D
    • 商用(商用测试) : 3.0.0
    packageName
    String
    应用软件的包名
    productId
    String
    In-App商品的商品ID
    messageType
    String
    SINGLE_PAYMENT_TRANSACTION 固定
    purchaseId
    String
    购买ID
    developerPayload
    String
    由开发公司管理以标识购买件的标识符
    purchaseTimeMillis
    Long
    在ONE store支付系统中完成支付的时间(ms)
    purcahseState
    String
    COMPLETED : 已支付 / CANCELED : 取消
    price
    String
    支付金额
    priceCurrencyCode
    String
    支付金额货币代码(KRW, USD, ...)
    productName
    String
    请求购买时,如开发公司设置了customized In-App商品标题则传达
    paymentTypeList
    List
    支付信息列表
    paymentMethod
    String
    amount
    String
    billingKey
    String
    用于扩展的付款密钥
    isTestMdn
    Boolean
    是否是测试机(true : 测试机, false : 非测试机)
    purchaseToken
    String
    购买token
    environment
    String
    支付环境
    • 开发(SANDBOX) : SANDBOX
    • 商用 :COMMERCIAL
    marketCode
    String
    市场分类编码 ( MKT_ONE : ONE store, MKT_GLB : Global ONE store)
    signature
    String
    此消息的signature
  • 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..."
}

paymentMethod(ONE store支付方式)定义

paymentMethod
支付方式名称
说明
DCB
手机支付
在运营商费用账单上以"信息使用费"项目收取
PHONEBILL
手机小额支付
在运营商费用账单上以"小额支付"项目收取
ONEPAY
ONE pay
ONE store提供的信用卡便捷支付
ONEPAYBANKACCT
ONE pay账户支付
ONE store提供的银行账户便捷支付
ONEPAYDCB
ONE pay手机支付
ONE store提供的手机便捷支付
ONEPAYPHONEBILL
ONE pay手机小额支付
ONE store提供的小额便捷支付
CREDITCARD
信用卡
一般信用卡支付
11PAY
11Pay
SK Plannet提供的信用卡便捷支付
NAVERPAY
N pay
Naver提供的Naver pay支付
CULTURELAND
Culture cash
韩国文化振兴提供的Culture cash支付
TMEMBERSHIP
T Membership
SK电讯提供的T Membership支付
OCB
OK cashbag
SK Plannet提供的OK cashbag支付
GAMECASH
Game cash
ONE store的Game cash支付
ONESTORECASH
ONE store cash
ONE store cash支付
ONESTORECOUPON
ONE store Coupon
ONE store Coupon支付
TMONEY
Mobile T Money
T-monet提供的Mobile T Money支付
KTMEMBERSHIP
KT Membership
KT Membership支付
LGMEMBERSHIP
U+Membership
LG U+Membership支付
EWALLET
e-Wallet
e-Wallet支付
BANKACCT
银行账户支付
一般银行账户支付
PAYPAL
PAYPAL
paypal支付

Signatue验证方法

使用下面的代码,您可以检查signature是否伪造。
  • "代码"中的PublicKey是指"开发者中心 > Apps > In-App信息 > 认证和许可 证"中提供的许可证密钥。 有关许可证密钥的详细信息,请参阅 "In-App支付的预准备页面的"验证许可证密钥和OAuth认证信息"部分。
PHP
Java
Python
<?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";
}
?>php
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));
}
}
# -*- 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()))

Subscription Notification消息发送规格(ONE store → 开发公司服务器)

  • URI : 在开发者中心设置的Subscription Notification URL
  • Method : POST
  • Request Parameters : N/A
  • Request Header
    Parameter Name
    Data Type
    Description
    Content-Type
    String
    application/json
  • Request Body : JSON格式
    Element Name
    Data Type
    Description
    msgVersion
    String
    消息版本
    • 开发(Sandbox) : 3.0.0D
    • 商用(商用测试) : 3.0.0
    packageName
    String
    应用软件的包名
    eventTimeMillis
    Long
    event发生时间
    subscriptionNotification
    Object
    支付信息目录
    version
    String
    订阅提醒消息版本
    notificationType
    Integer
    订阅状态
    purchaseToken
    String
    购买Token
    productId
    String
    In-App商品的商品ID
    environment
    String
    支付环境 · 开发(Sandbox) : SANDBOX · 商用 :COMMERCIAL
    marketCode
    String
    市场分类代码 ( MKT_ONE : ONE store, MKT_STM : storm )
  • Example
{
"msgVersion":"3.0.0",
"packageName":"com.onestore.pns",
"eventTimeMillis":24431212233000,
"subscriptionNotification": {
"version": "1",
"notificationType" : 1,
"purchaseToken":"TOKEN",
"productId": "com.product.id"
},
"environmenmt": "COMMERCIAL",
"marketCode": "MKT_ONE"
}

订阅状态定义

订阅状态
订阅代码
说明
1
SUBSCRIPTION_RECOVERED
定期支付已从保留状态恢复。
2
SUBSCRIPTION_RENEWED
已更新定期支付。
3
SUBSCRIPTION_CANCELED
客户要求解除定期支付。
4
SUBSCRIPTION_PURCHASED
您购买了新的定期支付商品。
5
SUBSCRIPTION_ON_HOLD
由于支付失败,定期支付处于保留状态。
6
SUBSCRIPTION_IN_GRACE_PERIOD
由于支付失败,定期支付处于延期状态。
7
SUBSCRIPTION_RESTARTED
客户取消了解除定期支付的要求。
8
SUBSCRIPTION_PRICE_CHANGE_CONFIRMED
用户同意变更定期支付的价格。
9
SUBSCRIPTION_DEFERRED
已延长定期支付的使用期限。
10
SUBSCRIPTION_PAUSED
已暂停定期支付。
11
SUBSCRIPTION_PAUSE_SCHEDULE_CHANGED
已变更暂停定期支付的日程。
12
SUBSCRIPTION_REVOKED
已立即解除定期支付。
13
SUBSCRIPTION_EXPIRED
定期支付已到期。

Notification传输方法

ONE store中的PNS服务器通过HTTP(S)请求向开发公司服务器发送notification。
此时,开发公司服务器应以200响应HTTP Status Code,表示已正常接收notification。
如果由于网络延迟而丢失,或者由于开发公司服务器的异常情况,HTTP Status Code未能以200进行响应,PNS服务器将认为notification传输失败,并在3天内最多执行30次重传。
Notification的重传是在具有一定delay后执行,如下例所示,当重试次数增多时,delay会逐渐增加。 Example
次数
delay (秒)
重传时间
0 (首次)
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 5mo ago