扫码支付

扫码支付除 isv_wap 外只需要服务端,不需要客户端 SDK,其他渠道的扫码支付,你的服务端需要做的就是向 Ping++ 请求 Charge 对象,然后获取 Charge 中 credential 字段里的二维码链接,并以此生成二维码。并且服务端监听和获取 Webhooks 通知,最后客户端需要轮询服务端的支付结果,具体步骤如下:

  1. 设置 API-Key
  2. SDK 验证签名设置
  3. 从服务端发起创建请求,获取 Charge 对象
  4. 根据渠道的不同选择是否返回 Charge 给客户端
  5. 接收 Webhooks 通知
  6. 验证 Webhooks 签名

第一步:设置 API-Key

Ping++ API 交易时需要设置 API-Key,Server SDK 提供了设置的方法。如果你直接使用 API ,需要在 header 中加入 Authorization,格式是 Authorization: Bearer API-Key。

\Pingpp\Pingpp::setApiKey('sk_test_ibbTe5jLGCi5rzfH4OqPW9KC');
Pingpp.apiKey = "sk_test_ibbTe5jLGCi5rzfH4OqPW9KC";
var pingpp = require('pingpp')('sk_test_ibbTe5jLGCi5rzfH4OqPW9KC');
pingpp.api_key = 'sk_test_ibbTe5jLGCi5rzfH4OqPW9KC'
Pingpp.api_key = "sk_test_ibbTe5jLGCi5rzfH4OqPW9KC"
pingpp.Key = "sk_test_ibbTe5jLGCi5rzfH4OqPW9KC"
Pingpp.Pingpp.SetApiKey("sk_test_ibbTe5jLGCi5rzfH4OqPW9KC");

第二步:SDK 验证签名设置

为了进一步增强交易请求的安全性,Ping++ 交易接口针对所有的 POST 和 PUT 请求已经新增 RSA 加密验签功能。如果使用该签名验证功能,你需要生成密钥,然后将私钥配置到你的代码中,公钥上传至 Ping++ 管理平台并启用验签开关。首先你需要本地生成 RSA 公钥和私钥,生成方法请参考:如何获取 RSA 公钥和私钥?

设置请求签名密钥

你需要在代码中设置请求签名的私钥(rsa_private_key.pem),可以读取配置私钥文件的路径或者直接定义变量。你如果通过 API 接口校验的话,需要生成 RSA 签名(SHA256)并在请求头中添加 Pingplusplus-Signature,如果使用 SDK 的话只需要配置私钥即可。

\Pingpp\Pingpp::setPrivateKeyPath(__DIR__ . '/your_rsa_private_key.pem');
Pingpp.privateKeyPath = "/path/to/your_rsa_private_key.pem";
pingpp.setPrivateKeyPath(__dirname + "/your_rsa_private_key.pem");
pingpp.private_key_path = 'your_rsa_private_key.pem'
Pingpp.private_key_path = File.dirname(__FILE__) + '/your_rsa_private_key.pem'
privateKey, err := ioutil.ReadFile("your_rsa_private_key.pem")
Pingpp.Pingpp.SetPrivateKeyPath(@"../../your_rsa_private_key.pem");

上传公钥至 Ping++ 管理平台

设置完代码中的私钥,你需要将已经生成的公钥(rsa_public_key.pem)填写到 Ping++ 管理平台上。 配置路径: 登录 Ping++ 管理平台->点击右上角公司名称->企业面板->开发参数->商户 RSA 公钥->将你的公钥复制粘贴进去并且保存->先启用 Test 模式进行测试->测试通过后启用 Live 模式

rsa_keys_setting

注意: 一旦上传公钥至 Ping++ 管理平台并启用 Live 模式,则验证签名功能即时生效,Ping++ 会立即验证你的真实线上交易验签请求。如果私钥为空或错误,则会交易失败,所以请确保测试模式正常后再启用 Live 开关。

第三步:从服务端发起创建请求,获取 Charge 对象

调用 Ping++ Server SDK 发起支付请求,发起请求所需参数具体可参考 API 文档,不同的渠道只需要切换 channel以及对应的 extra 参数即可。

alipay_qr
\Pingpp\Charge::create(array(
'order_no' => '123456789',
'amount' => '100',//订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
'app' => array('id' => 'app_1Gqj58ynP0mHeX1q'),
'channel' => 'alipay_qr',
'currency' => 'cny',
'client_ip' => '127.0.0.1',
'subject' => 'Your Subject',
'body' => 'Your Body'
));
Map<String, Object> chargeParams = new HashMap<String, Object>();
chargeParams.put("order_no", "123456789");
chargeParams.put("amount", 100);//订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
Map<String, String> app = new HashMap<String, String>();
app.put("id", "app_1Gqj58ynP0mHeX1q");
chargeParams.put("app", app);
chargeParams.put("channel", "alipay_qr");
chargeParams.put("currency", "cny");
chargeParams.put("client_ip", "127.0.0.1");
chargeParams.put("subject", "Your Subject");
chargeParams.put("body", "Your Body");
Charge.create(chargeParams);
pingpp.charges.create({
subject: "Your Subject",
body: "Your Body",
amount: 100,//订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
order_no: "123456789",
channel: "alipay_qr",
currency: "cny",
client_ip: "127.0.0.1",
app: {id: "app_1Gqj58ynP0mHeX1q"}
}, function(err, charge) {
// YOUR CODE
});
ch = pingpp.Charge.create(
order_no='123456789',
amount=100, #订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
app=dict(id='app_1Gqj58ynP0mHeX1q'),
channel='alipay_qr',
currency='cny',
client_ip='127.0.0.1',
subject='Your Subject',
body='Your Body'
)
Pingpp::Charge.create(
:order_no => "123456789",
:amount => 100,# 订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
:subject => "Your Subject",
:body => "Your Body",
:channel => "alipay_qr",
:currency => "cny",
:client_ip=> "127.0.0.1",
:app => {:id => "app_1Gqj58ynP0mHeX1q"}
)
params := &pingpp.ChargeParams{
Order_no: "123456789",
App: pingpp.App{Id: "app_1Gqj58ynP0mHeX1q"},
Amount: 100,//订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
Channel: "alipay_qr",
Currency: "cny",
Client_ip: "127.0.0.1",
Subject: "Your Subject",
Body: "Your Body",
}
//返回的第一个参数是 charge 对象,你需要将其转换成 json 给客户端,或者客户端接收后转换。
ch, err := charge.New(params)
var chParams = new Dictionary<string, object>
{
{"order_no", "123456789"},
{"amount", 100},//订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
{"channel", "alipay_qr"},
{"currency", "cny"},
{"subject", "Your Subject"},
{"body", "Your Body"},
{"client_ip", "127.0.0.1"},
{"app", new Dictionary<string, string> {{"id", "app_1Gqj58ynP0mHeX1q"}}}
};
var ch = Charge.Create(chParams);
wx_pub_qr
\Pingpp\Charge::create(array(
'order_no' => '123456789',
'amount' => '100',//订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
'app' => array('id' => 'app_1Gqj58ynP0mHeX1q'),
'channel' => 'wx_pub_qr',
'currency' => 'cny',
'client_ip' => '127.0.0.1',
'subject' => 'Your Subject',
'body' => 'Your Body',
'extra' => array('product_id' => 'Your ProductId')
));
Map<String, Object> chargeParams = new HashMap<String, Object>();
chargeParams.put("order_no", "123456789");
chargeParams.put("amount", 100);//订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
Map<String, String> app = new HashMap<String, String>();
app.put("id", "app_1Gqj58ynP0mHeX1q");
chargeParams.put("app", app);
chargeParams.put("channel", "wx_pub_qr");
chargeParams.put("currency", "cny");
chargeParams.put("client_ip", "127.0.0.1");
chargeParams.put("subject", "Your Subject");
chargeParams.put("body", "Your Body");
Map<String, String> extra = new HashMap<String, String>();
extra.put("product_id", "Your ProductId");
chargeParams.put("extra", extra);
Charge.create(chargeParams);
pingpp.charges.create({
subject: "Your Subject",
body: "Your Body",
amount: 100,//订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
order_no: "123456789",
channel: "wx_pub_qr",
currency: "cny",
client_ip: "127.0.0.1",
app: {id: "app_1Gqj58ynP0mHeX1q"},
extra: {product_id: "Your ProductId"}
}, function(err, charge) {
// YOUR CODE
});
ch = pingpp.Charge.create(
order_no='123456789',
amount=100, #订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
app=dict(id='app_1Gqj58ynP0mHeX1q'),
channel='wx_pub_qr',
currency='cny',
client_ip='127.0.0.1',
subject='Your Subject',
body='Your Body',
extra=dict(product_id='Your ProductId')
)
Pingpp::Charge.create(
:order_no => "123456789",
:amount => 100,# 订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
:subject => "Your Subject",
:body => "Your Body",
:channel => "wx_pub_qr",
:currency => "cny",
:client_ip=> "127.0.0.1",
:app => {:id => "app_1Gqj58ynP0mHeX1q"},
:extra => {:product_id => "Your ProductId"}
)
extra := make(map[string]interface{})
extra["product_id"] = "User ProductId"
params := &pingpp.ChargeParams{
Order_no: "123456789",
App: pingpp.App{Id: "app_1Gqj58ynP0mHeX1q"},
Amount: 100,//订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
Channel: "wx_pub_qr",
Currency: "cny",
Client_ip: "127.0.0.1",
Subject: "Your Subject",
Body: "Your Body",
Extra: extra,
}
//返回的第一个参数是 charge 对象,你需要将其转换成 json 给客户端,或者客户端接收后转换。
ch, err := charge.New(params)
var chParams = new Dictionary<string, object>
{
{"order_no", "123456789"},
{"amount", 100},//订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
{"channel", "wx_pub_qr"},
{"currency", "cny"},
{"subject", "Your Subject"},
{"body", "Your Body"},
{"client_ip", "127.0.0.1"},
{"app", new Dictionary<string, string> {{"id", "app_1Gqj58ynP0mHeX1q"}}},
{"extra", new Dictionary<string, object> {{"product_id", "Your ProductId"}}}
};
cb_alipay_qr
\Pingpp\Charge::create(array(
'order_no' => '123456789',
'amount' => '100',//订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
'app' => array('id' => 'app_1Gqj58ynP0mHeX1q'),
'channel' => 'cb_alipay_qr',
'currency' => 'usd',
'client_ip' => '127.0.0.1',
'subject' => 'Your Subject',
'body' => 'Your Body'
));
Map<String, Object> chargeParams = new HashMap<String, Object>();
chargeParams.put("order_no", "123456789");
chargeParams.put("amount", 100);//订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
Map<String, String> app = new HashMap<String, String>();
app.put("id", "app_1Gqj58ynP0mHeX1q");
chargeParams.put("app", app);
chargeParams.put("channel", "cb_alipay_qr");
chargeParams.put("currency", "usd");
chargeParams.put("client_ip", "127.0.0.1");
chargeParams.put("subject", "Your Subject");
chargeParams.put("body", "Your Body");
Charge.create(chargeParams);
pingpp.charges.create({
subject: "Your Subject",
body: "Your Body",
amount: 100,//订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
order_no: "123456789",
channel: "cb_alipay_qr",
currency: "usd",
client_ip: "127.0.0.1",
app: {id: "app_1Gqj58ynP0mHeX1q"}
}, function(err, charge) {
// YOUR CODE
});
ch = pingpp.Charge.create(
order_no='123456789',
amount=100, #订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
app=dict(id='app_1Gqj58ynP0mHeX1q'),
channel='cb_alipay_qr',
currency='usd',
client_ip='127.0.0.1',
subject='Your Subject',
body='Your Body'
)
Pingpp::Charge.create(
:order_no => "123456789",
:amount => 100,# 订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
:subject => "Your Subject",
:body => "Your Body",
:channel => "cb_alipay_qr",
:currency => "usd",
:client_ip=> "127.0.0.1",
:app => {:id => "app_1Gqj58ynP0mHeX1q"}
)
params := &pingpp.ChargeParams{
Order_no: "123456789",
App: pingpp.App{Id: "app_1Gqj58ynP0mHeX1q"},
Amount: 100,//订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
Channel: "cb_alipay_qr",
Currency: "usd",
Client_ip: "127.0.0.1",
Subject: "Your Subject",
Body: "Your Body",
}
//返回的第一个参数是 charge 对象,你需要将其转换成 json 给客户端,或者客户端接收后转换。
ch, err := charge.New(params)
var chParams = new Dictionary<string, object>
{
{"order_no", "123456789"},
{"amount", 100},//订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
{"channel", "cb_alipay_qr"},
{"currency", "usd"},
{"subject", "Your Subject"},
{"body", "Your Body"},
{"client_ip", "127.0.0.1"},
{"app", new Dictionary<string, string> {{"id", "app_1Gqj58ynP0mHeX1q"}}}
};
var ch = Charge.Create(chParams);
cb_wx_pub_qr
\Pingpp\Charge::create(array(
'order_no' => '123456789',
'amount' => '100',//订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
'app' => array('id' => 'app_1Gqj58ynP0mHeX1q'),
'channel' => 'cb_wx_pub_qr',
'currency' => 'usd',
'client_ip' => '127.0.0.1',
'subject' => 'Your Subject',
'body' => 'Your Body',
'extra' => array(
'limit_pay' => 'no_credit',
'product_id' => 'your product id',
'goods_list' => array(
'goods_name' => 'iPhone',
'goods_num' => '1'
)
)
));
Map<String, Object> chargeParams = new HashMap<String, Object>();
chargeParams.put("order_no", "123456789");
chargeParams.put("amount", 100);//订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
Map<String, String> app = new HashMap<String, String>();
app.put("id", "app_1Gqj58ynP0mHeX1q");
chargeParams.put("app", app);
chargeParams.put("channel", "cb_wx_pub_qr");
chargeParams.put("currency", "usd");
chargeParams.put("client_ip", "127.0.0.1");
chargeParams.put("subject", "Your Subject");
chargeParams.put("body", "Your Body");
Map<String, String> extra = new HashMap<String, String>();
extra.put("product_id", "Your ProductId");
extra.put("limit_pay", "no_credit");
// 必填,商品列表
List<Object> goodsList = goodsListForCbWx();
extra.put("goods_list", goodsList);
chargeParams.put("extra", extra);
Charge.create(chargeParams);
pingpp.charges.create({
subject: "Your Subject",
body: "Your Body",
amount: 100,//订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
order_no: "123456789",
channel: "cb_wx_pub_qr",
currency: "usd",
client_ip: "127.0.0.1",
app: {id: "app_1Gqj58ynP0mHeX1q"},
extra: {
product_id: "Your ProductId",
limit_pay: "no_credit",
goods_list: goods_list // 商品列表数组
}
}, function(err, charge) {
// YOUR CODE
});
ch = pingpp.Charge.create(
order_no='123456789',
amount=100, #订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
app=dict(id='app_1Gqj58ynP0mHeX1q'),
channel='cb_wx_pub_qr',
currency='usd',
client_ip='127.0.0.1',
subject='Your Subject',
body='Your Body',
extra=dict(
product_id='Your ProductId',
limit_pay='no_credit',
goods_list = goods_list #商品列表数组
)
)
Pingpp::Charge.create(
:order_no => "123456789",
:amount => 100,# 订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
:subject => "Your Subject",
:body => "Your Body",
:channel => "cb_wx_pub_qr",
:currency => "usd",
:client_ip=> "127.0.0.1",
:app => {:id => "app_1Gqj58ynP0mHeX1q"},
:extra => {
:product_id => "Your ProductId",
:limit_pay => "no_credit",
:goods_list => goods_list #商品列表数组
}
)
extra := make(map[string]interface{})
extra["product_id"] = "User ProductId"
extra["limit_pay"] = "no_credit"
extra["goods_list"] = goods_list //商品列表数组
params := &pingpp.ChargeParams{
Order_no: "123456789",
App: pingpp.App{Id: "app_1Gqj58ynP0mHeX1q"},
Amount: 100,//订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
Channel: "cb_wx_pub_qr",
Currency: "usd",
Client_ip: "127.0.0.1",
Subject: "Your Subject",
Body: "Your Body",
Extra: extra,
}
//返回的第一个参数是 charge 对象,你需要将其转换成 json 给客户端,或者客户端接收后转换。
ch, err := charge.New(params)
var chParams = new Dictionary<string, object>
{
{"order_no", "123456789"},
{"amount", 100},//订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
{"channel", "cb_wx_pub_qr"},
{"currency", "usd"},
{"subject", "Your Subject"},
{"body", "Your Body"},
{"client_ip", "127.0.0.1"},
{"app", new Dictionary<string, string> {{"id", "app_1Gqj58ynP0mHeX1q"}}},
{"extra", new Dictionary<string, object> {
{"product_id", "Your ProductId"},
{"limit_pay", "no_credit"},
{"goods_list": goods_list} //商品列表数组
}
}
};
upacp_qr
\Pingpp\Charge::create(array(
'order_no' => '123456789',
'amount' => '100',//订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
'app' => array('id' => 'app_1Gqj58ynP0mHeX1q'),
'channel' => 'upacp_qr',
'currency' => 'cny',
'client_ip' => '127.0.0.1',
'subject' => 'Your Subject',
'body' => 'Your Body',
'extra' => array('result_url' => 'http://example.com/result')
));
Map<String, Object> chargeParams = new HashMap<String, Object>();
chargeParams.put("order_no", "123456789");
chargeParams.put("amount", 100);//订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
Map<String, String> app = new HashMap<String, String>();
app.put("id", "app_1Gqj58ynP0mHeX1q");
chargeParams.put("app", app);
chargeParams.put("channel", "upacp_qr");
chargeParams.put("currency", "cny");
chargeParams.put("client_ip", "127.0.0.1");
chargeParams.put("subject", "Your Subject");
chargeParams.put("body", "Your Body");
Map<String, String> extra = new HashMap<String, String>();
extra.put("result_url", "http://example.com/result");
chargeParams.put("extra", extra);
Charge.create(chargeParams);
pingpp.charges.create({
subject: "Your Subject",
body: "Your Body",
amount: 100,//订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
order_no: "123456789",
channel: "upacp_qr",
currency: "cny",
client_ip: "127.0.0.1",
app: {id: "app_1Gqj58ynP0mHeX1q"},
extra: {result_url: "http://example.com/result"}
}, function(err, charge) {
// YOUR CODE
});
ch = pingpp.Charge.create(
order_no='123456789',
amount=100, #订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
app=dict(id='app_1Gqj58ynP0mHeX1q'),
channel='upacp_qr',
currency='cny',
client_ip='127.0.0.1',
subject='Your Subject',
body='Your Body',
extra=dict(result_url='http://example.com/result')
)
Pingpp::Charge.create(
:order_no => "123456789",
:amount => 100,# 订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
:subject => "Your Subject",
:body => "Your Body",
:channel => "upacp_qr",
:currency => "cny",
:client_ip=> "127.0.0.1",
:app => {:id => "app_1Gqj58ynP0mHeX1q"},
:extra => {:result_url => "http://example.com/result"}
)
extra := make(map[string]interface{})
extra["result_url"] = "http://example.com/result"
params := &pingpp.ChargeParams{
Order_no: "123456789",
App: pingpp.App{Id: "app_1Gqj58ynP0mHeX1q"},
Amount: 100,//订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
Channel: "upacp_qr",
Currency: "cny",
Client_ip: "127.0.0.1",
Subject: "Your Subject",
Body: "Your Body",
Extra: extra,
}
//返回的第一个参数是 charge 对象,你需要将其转换成 json 给客户端,或者客户端接收后转换。
ch, err := charge.New(params)
var chParams = new Dictionary<string, object>
{
{"order_no", "123456789"},
{"amount", 100},//订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
{"channel", "upacp_qr"},
{"currency", "cny"},
{"subject", "Your Subject"},
{"body", "Your Body"},
{"client_ip", "127.0.0.1"},
{"app", new Dictionary<string, string> {{"id", "app_1Gqj58ynP0mHeX1q"}}},
{"extra", new Dictionary<string, object> {{"result_url", "http://example.com/result"}}}
};
var ch = Charge.Create(chParams);
ccb_qr
\Pingpp\Charge::create(array(
'order_no' => '123456789',
'amount' => '100',//订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
'app' => array('id' => 'app_1Gqj58ynP0mHeX1q'),
'channel' => 'ccb_qr',
'currency' => 'cny',
'client_ip' => '127.0.0.1',
'subject' => 'Your Subject',
'body' => 'Your Body',
'extra' => array(
'pos_id' => '025547632',
'remark' => 'test',
'return_type' => 1)
));
Map<String, Object> chargeParams = new HashMap<String, Object>();
chargeParams.put("order_no", "123456789");
chargeParams.put("amount", 100);//订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
Map<String, String> app = new HashMap<String, String>();
app.put("id", "app_1Gqj58ynP0mHeX1q");
chargeParams.put("app", app);
chargeParams.put("channel", "ccb_qr");
chargeParams.put("currency", "cny");
chargeParams.put("client_ip", "127.0.0.1");
chargeParams.put("subject", "Your Subject");
chargeParams.put("body", "Your Body");
Map<String, String> extra = new HashMap<String, String>();
extra.put("pos_id", "025547632");
extra.put("remark", "test",);
extra.put("return_type", 1,);
chargeParams.put("extra", extra);
Charge.create(chargeParams);
pingpp.charges.create({
subject: "Your Subject",
body: "Your Body",
amount: 100,//订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
order_no: "123456789",
channel: "ccb_qr",
currency: "cny",
client_ip: "127.0.0.1",
app: {id: "app_1Gqj58ynP0mHeX1q"},
extra: {
pos_id: "025547632",
remark: "test",
return_type:1
}
}, function(err, charge) {
// YOUR CODE
});
ch = pingpp.Charge.create(
order_no='123456789',
amount=100, #订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
app=dict(id='app_1Gqj58ynP0mHeX1q'),
channel='ccb_qr',
currency='cny',
client_ip='127.0.0.1',
subject='Your Subject',
body='Your Body',
extra=dict(pos_id ='025547632', remark ='test',return_type = 1)
)
Pingpp::Charge.create(
:order_no => "123456789",
:amount => 100,# 订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
:subject => "Your Subject",
:body => "Your Body",
:channel => "ccb_qr",
:currency => "cny",
:client_ip=> "127.0.0.1",
:app => {:id => "app_1Gqj58ynP0mHeX1q"},
:extra => {
: pos_id => '025547632',
: remark => 'test',
: return_type => 1
}
)
extra := make(map[string]interface{})
extra["pos_id"] = "025547632"
extra["remark"] = "test"
extra["return_type"] = 1
params := &pingpp.ChargeParams{
Order_no: "123456789",
App: pingpp.App{Id: "app_1Gqj58ynP0mHeX1q"},
Amount: 100,//订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
Channel: "ccb_qr",
Currency: "cny",
Client_ip: "127.0.0.1",
Subject: "Your Subject",
Body: "Your Body",
Extra: extra,
}
//返回的第一个参数是 charge 对象,你需要将其转换成 json 给客户端,或者客户端接收后转换。
ch, err := charge.New(params)
var chParams = new Dictionary<string, object>
{
{"order_no", "123456789"},
{"amount", 100},//订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
{"channel", "ccb_qr"},
{"currency", "cny"},
{"subject", "Your Subject"},
{"body", "Your Body"},
{"client_ip", "127.0.0.1"},
{"app", new Dictionary<string, string> {{"id", "app_1Gqj58ynP0mHeX1q"}}},
{"extra", new Dictionary<string, object> {
{"pos_id", "025547632"},
{"remark": "test"},
{"return_type":1}
}}
};
var ch = Charge.Create(chParams);
isv_qr
\Pingpp\Charge::create(array(
'order_no' => '123456789',
'amount' => '200000',//订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
'app' => array('id' => 'app_1Gqj58ynP0mHeX1q'),
'channel' => 'isv_qr',
'currency' => 'cny',
'client_ip' => '127.0.0.1',
'subject' => 'Your Subject',
'body' => 'Your Body',
'extra' => array(
'pay_channel' => 'wx',
'terminal_id' => 'T0000001'
)
));
Map<String, Object> chargeParams = new HashMap<String, Object>();
chargeParams.put("order_no", "123456789");
chargeParams.put("amount", 200000);//订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
Map<String, String> app = new HashMap<String, String>();
app.put("id", "app_1Gqj58ynP0mHeX1q");
chargeParams.put("app", app);
chargeParams.put("channel", "isv_qr");
chargeParams.put("currency", "cny");
chargeParams.put("client_ip", "127.0.0.1");
chargeParams.put("subject", "Your Subject");
chargeParams.put("body", "Your Body");
Map<String, String> extra = new HashMap<String, String>();
extra.put("pay_channel", "wz");
extra.put("terminal_id", "T0000001");
chargeParams.put("extra", extra);
Charge.create(chargeParams);
pingpp.charges.create({
subject: "Your Subject",
body: "Your Body",
amount: 200000,//订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
order_no: "123456789",
channel: "isv_qr",
currency: "cny",
client_ip: "127.0.0.1",
app: {id: "app_1Gqj58ynP0mHeX1q"},
extra: {
pay_channel : "wx",
terminal_id : "T0000001"
}
}, function(err, charge) {
// YOUR CODE
});
ch = pingpp.Charge.create(
order_no='123456789',
amount= 200000, #订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
app=dict(id='app_1Gqj58ynP0mHeX1q'),
channel='isv_qr',
currency='cny',
client_ip='127.0.0.1',
subject='Your Subject',
body='Your Body',
extra=dict(
terminal_id ='T0000001',
pay_channel ='wx'
)
)
Pingpp::Charge.create(
:order_no => "123456789",
:amount => 200000,# 订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
:subject => "Your Subject",
:body => "Your Body",
:channel => "isv_qr",
:currency => "cny",
:client_ip=> "127.0.0.1",
:app => {:id => "app_1Gqj58ynP0mHeX1q"},
:extra => {
: pay_channel => "wx",
: terminal_id => "T0000001"
}
)
extra := make(map[string]interface{})
extra["pay_channel"] = "wx"
extra["terminal_id"] = "T0000001"
params := &pingpp.ChargeParams{
Order_no: "123456789",
App: pingpp.App{Id: "app_1Gqj58ynP0mHeX1q"},
Amount: 200000,//订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
Channel: "isv_qr",
Currency: "cny",
Client_ip: "127.0.0.1",
Subject: "Your Subject",
Body: "Your Body",
Extra: extra,
}
//返回的第一个参数是 charge 对象,你需要将其转换成 json 给客户端,或者客户端接收后转换。
ch, err := charge.New(params)
var chParams = new Dictionary<string, object>
{
{"order_no", "123456789"},
{"amount", 200000},//订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
{"channel", "isv_qr"},
{"currency", "cny"},
{"subject", "Your Subject"},
{"body", "Your Body"},
{"client_ip", "127.0.0.1"},
{"app", new Dictionary<string, string> {{"id", "app_1Gqj58ynP0mHeX1q"}}},
{"extra", new Dictionary<string, object> {
{"pay_channel", "wx"},
{"terminal_id", "T0000001"}
}}
};
var ch = Charge.Create(chParams);
isv_wap
\Pingpp\Charge::create(array(
'order_no' => '123456789',
'amount' => '200000',//订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
'app' => array('id' => 'app_1Gqj58ynP0mHeX1q'),
'channel' => 'isv_wap',
'currency' => 'cny',
'client_ip' => '127.0.0.1',
'subject' => 'Your Subject',
'body' => 'Your Body',
'extra' => array(
'pay_channel' => 'alipay',
'terminal_id' => 'T0000001',
'result_url' => 'https://www.example.com/payment-result'
)
));
Map<String, Object> chargeParams = new HashMap<String, Object>();
chargeParams.put("order_no", "123456789");
chargeParams.put("amount", 200000);//订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
Map<String, String> app = new HashMap<String, String>();
app.put("id", "app_1Gqj58ynP0mHeX1q");
chargeParams.put("app", app);
chargeParams.put("channel", "isv_wap");
chargeParams.put("currency", "cny");
chargeParams.put("client_ip", "127.0.0.1");
chargeParams.put("subject", "Your Subject");
chargeParams.put("body", "Your Body");
Map<String, String> extra = new HashMap<String, String>();
extra.put("pay_channel", "alipay");
extra.put("terminal_id", "T0000001");
extra.put("result_url", "https://www.example.com/payment-result");
chargeParams.put("extra", extra);
Charge.create(chargeParams);
pingpp.charges.create({
subject: "Your Subject",
body: "Your Body",
amount: 200000,//订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
order_no: "123456789",
channel: "isv_wap",
currency: "cny",
client_ip: "127.0.0.1",
app: {id: "app_1Gqj58ynP0mHeX1q"},
extra: {
pay_channel : "alipay",
terminal_id : "T0000001",
result_url : "https://www.example.com/payment-result"
}
}, function(err, charge) {
// YOUR CODE
});
ch = pingpp.Charge.create(
order_no='123456789',
amount= 200000, #订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
app=dict(id='app_1Gqj58ynP0mHeX1q'),
channel='isv_wap',
currency='cny',
client_ip='127.0.0.1',
subject='Your Subject',
body='Your Body',
extra=dict(
terminal_id ='T0000001',
pay_channel ='alipay',
result_url = 'https://www.example.com/payment-result'
)
)
Pingpp::Charge.create(
:order_no => "123456789",
:amount => 200000,# 订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
:subject => "Your Subject",
:body => "Your Body",
:channel => "isv_wap",
:currency => "cny",
:client_ip=> "127.0.0.1",
:app => {:id => "app_1Gqj58ynP0mHeX1q"},
:extra => {
: pay_channel => "alipay",
: terminal_id => "T0000001",
: result_url => "https://www.example.com/payment-result"
}
)
extra := make(map[string]interface{})
extra["pay_channel"] = "alipay"
extra["terminal_id"] = "T0000001"
extra["result_url"] = "https://www.example.com/payment-result"
params := &pingpp.ChargeParams{
Order_no: "123456789",
App: pingpp.App{Id: "app_1Gqj58ynP0mHeX1q"},
Amount: 200000,//订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
Channel: "isv_wap",
Currency: "cny",
Client_ip: "127.0.0.1",
Subject: "Your Subject",
Body: "Your Body",
Extra: extra,
}
//返回的第一个参数是 charge 对象,你需要将其转换成 json 给客户端,或者客户端接收后转换。
ch, err := charge.New(params)
var chParams = new Dictionary<string, object>
{
{"order_no", "123456789"},
{"amount", 200000},//订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
{"channel", "isv_wap"},
{"currency", "cny"},
{"subject", "Your Subject"},
{"body", "Your Body"},
{"client_ip", "127.0.0.1"},
{"app", new Dictionary<string, string> {{"id", "app_1Gqj58ynP0mHeX1q"}}},
{"extra", new Dictionary<string, object> {
{"pay_channel", "alipay"},
{"terminal_id", "T0000001"},
{"result_url", "https://www.example.com/payment-result"}
}}
};
var ch = Charge.Create(chParams);
isv_scan
\Pingpp\Charge::create(array(
'order_no' => '123456789',
'amount' => '200000',//订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
'app' => array('id' => 'app_1Gqj58ynP0mHeX1q'),
'channel' => 'isv_scan',
'currency' => 'cny',
'client_ip' => '127.0.0.1',
'subject' => 'Your Subject',
'body' => 'Your Body',
'extra' => array(
'scan_code' => '131141564583769183',
'terminal_id' => 'T0000001'
)
));
Map<String, Object> chargeParams = new HashMap<String, Object>();
chargeParams.put("order_no", "123456789");
chargeParams.put("amount", 200000);//订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
Map<String, String> app = new HashMap<String, String>();
app.put("id", "app_1Gqj58ynP0mHeX1q");
chargeParams.put("app", app);
chargeParams.put("channel", "isv_scan");
chargeParams.put("currency", "cny");
chargeParams.put("client_ip", "127.0.0.1");
chargeParams.put("subject", "Your Subject");
chargeParams.put("body", "Your Body");
Map<String, String> extra = new HashMap<String, String>();
extra.put("scan_code", "131141564583769183");
extra.put("terminal_id", "T0000001");
chargeParams.put("extra", extra);
Charge.create(chargeParams);
pingpp.charges.create({
subject: "Your Subject",
body: "Your Body",
amount: 200000,//订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
order_no: "123456789",
channel: "isv_scan",
currency: "cny",
client_ip: "127.0.0.1",
app: {id: "app_1Gqj58ynP0mHeX1q"},
extra: {
scan_code : "131141564583769183",
terminal_id : "T0000001"
}
}, function(err, charge) {
// YOUR CODE
});
ch = pingpp.Charge.create(
order_no='123456789',
amount= 200000, #订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
app=dict(id='app_1Gqj58ynP0mHeX1q'),
channel='isv_scan',
currency='cny',
client_ip='127.0.0.1',
subject='Your Subject',
body='Your Body',
extra=dict(
terminal_id ='T0000001',
scan_code ='131141564583769183'
)
)
Pingpp::Charge.create(
:order_no => "123456789",
:amount => 200000,# 订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
:subject => "Your Subject",
:body => "Your Body",
:channel => "isv_scan",
:currency => "cny",
:client_ip=> "127.0.0.1",
:app => {:id => "app_1Gqj58ynP0mHeX1q"},
:extra => {
: scan_code => "131141564583769183",
: terminal_id => "T0000001"
}
)
extra := make(map[string]interface{})
extra["scan_code"] = "131141564583769183"
extra["terminal_id"] = "T0000001"
params := &pingpp.ChargeParams{
Order_no: "123456789",
App: pingpp.App{Id: "app_1Gqj58ynP0mHeX1q"},
Amount: 200000,//订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
Channel: "isv_scan",
Currency: "cny",
Client_ip: "127.0.0.1",
Subject: "Your Subject",
Body: "Your Body",
Extra: extra,
}
//返回的第一个参数是 charge 对象,你需要将其转换成 json 给客户端,或者客户端接收后转换。
ch, err := charge.New(params)
var chParams = new Dictionary<string, object>
{
{"order_no", "123456789"},
{"amount", 200000},//订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
{"channel", "isv_scan"},
{"currency", "cny"},
{"subject", "Your Subject"},
{"body", "Your Body"},
{"client_ip", "127.0.0.1"},
{"app", new Dictionary<string, string> {{"id", "app_1Gqj58ynP0mHeX1q"}}},
{"extra", new Dictionary<string, object> {
{"scan_code", "131141564583769183"},
{"terminal_id", "T0000001"}
}}
};
var ch = Charge.Create(chParams);

Ping++ 收到支付请求后返回给你的服务器一个 Charge 对象,我们称这个 Charge 对象为支付凭据。下面是支付凭据的一个示例:

alipay_qr
{
"id": "ch_PuHG84GqL8SOarj5iPuv5q9O",
"object": "charge",
"created": 1458186741,
"livemode": true,
"paid": false,
"refunded": false,
"app": "app_1Gqj58ynP0mHeX1q",
"channel": "alipay_qr",
"order_no": "123456789",
"client_ip": "127.0.0.1",
"amount": 100,
"amount_settle": 100,
"currency": "cny",
"subject": "Your Subject",
"body": "Your Body",
"extra": {
},
"time_paid": null,
"time_expire": 1458273141,
"time_settle": null,
"transaction_no": null,
"refunds": {
"object": "list",
"url": "/v1/charges/ch_PuHG84GqL8SOarj5iPuv5q9O/refunds",
"has_more": false,
"data": [
]
},
"amount_refunded": 0,
"failure_code": null,
"failure_msg": null,
"metadata": {
},
"credential": {
"object": "credential",
"alipay_qr": "https://qr.alipay.com/tax09784bfcqujv2upup2070"
},
"description": "Your Description"
}
wx_pub_qr
{
"id": "ch_TuHOq1vrfTy9HSyTWT9yTmXL",
"object": "charge",
"created": 1458187678,
"livemode": true,
"paid": false,
"refunded": false,
"app": "app_1Gqj58ynP0mHeX1q",
"channel": "wx_pub_qr",
"order_no": "123456789",
"client_ip": "127.0.0.1",
"amount": 100,
"amount_settle": 100,
"currency": "cny",
"subject": "Your Subject",
"body": "Your Body",
"extra": {
"product_id": "Product4Testing"
},
"time_paid": null,
"time_expire": 1458194878,
"time_settle": null,
"transaction_no": null,
"refunds": {
"object": "list",
"url": "/v1/charges/ch_TuHOq1vrfTy9HSyTWT9yTmXL/refunds",
"has_more": false,
"data": [
]
},
"amount_refunded": 0,
"failure_code": null,
"failure_msg": null,
"metadata": {
},
"credential": {
"object": "credential",
"wx_pub_qr": "weixin://wxpay/bizpayurl?pr=qnZDTZm"
},
"description": "Your Description"
}
cb_alipay_qr
{
"id": "ch_TuHOq1vrfTy9HSyTWT9yTmXL",
"object": "charge",
"created": 1458187678,
"livemode": true,
"paid": false,
"refunded": false,
"app": "app_1Gqj58ynP0mHeX1q",
"channel": "cb_wx_pub_qr",
"order_no": "123456789",
"client_ip": "127.0.0.1",
"amount": 100,
"amount_settle": 100,
"currency": "usd",
"subject": "Your Subject",
"body": "Your Body",
"extra": {
"product_id": "Product4Testing",
"goods_list": [
{
"goods_name": "iPhone",
"goods_num": "1"
}
],
"limit_pay": "no_credit"
},
"time_paid": null,
"time_expire": 1458194878,
"time_settle": null,
"transaction_no": null,
"refunds": {
"object": "list",
"url": "/v1/charges/ch_TuHOq1vrfTy9HSyTWT9yTmXL/refunds",
"has_more": false,
"data": [
]
},
"amount_refunded": 0,
"failure_code": null,
"failure_msg": null,
"metadata": {
},
"credential": {
"object": "credential",
"cb_wx_pub_qr": "weixin://wxpay/bizpayurl?pr=qnZDTZm"
},
"description": "Your Description"
}
cb_wx_pub_qr
{
"id": "ch_TuHOq1vrfTy9HSyTWT9yTmXL",
"object": "charge",
"created": 1458187678,
"livemode": true,
"paid": false,
"refunded": false,
"app": "app_1Gqj58ynP0mHeX1q",
"channel": "cb_wx_pub_qr",
"order_no": "123456789",
"client_ip": "127.0.0.1",
"amount": 100,
"amount_settle": 100,
"currency": "usd",
"subject": "Your Subject",
"body": "Your Body",
"extra": {
"product_id": "Product4Testing",
"goods_list": [
{
"goods_name": "iPhone",
"goods_num": "1"
}
],
"limit_pay": "no_credit"
},
"time_paid": null,
"time_expire": 1458194878,
"time_settle": null,
"transaction_no": null,
"refunds": {
"object": "list",
"url": "/v1/charges/ch_TuHOq1vrfTy9HSyTWT9yTmXL/refunds",
"has_more": false,
"data": [
]
},
"amount_refunded": 0,
"failure_code": null,
"failure_msg": null,
"metadata": {
},
"credential": {
"object": "credential",
"cb_wx_pub_qr": "weixin://wxpay/bizpayurl?pr=qnZDTZm"
},
"description": "Your Description"
}
upacp_qr
{
"id": "ch_Ce1q1CDSaPaTz9efP0afH4aH",
"object": "charge",
"created": 1539237945,
"livemode": true,
"paid": false,
"refunded": false,
"reversed": false,
"app": "app_1Gqj58ynP0mHeX1q",
"channel": "upacp_qr",
"order_no": "201801009425",
"client_ip": "180.168.5.158",
"amount": 1,
"amount_settle": -9,
"currency": "cny",
"subject": "简米测试订单478",
"body": "订单内容",
"extra": {
"result_url": "https://www.pingxx.com"
},
"time_paid": null,
"time_expire": 1539324345,
"time_settle": null,
"transaction_no": null,
"refunds": {
"object": "list",
"url": "/v1/charges/ch_Ce1q1CDSaPaTz9efP0afH4aH/refunds",
"has_more": false,
"data": []
},
"amount_refunded": 0,
"failure_code": null,
"failure_msg": null,
"metadata": {},
"credential": {
"object": "credential",
"upacp_qr": "https://qr.95516.com/00010001/62262xxx"
},
"description": null
}
ccb_qr
{
"id": "ch_bbnT0G44O088Hyzn94KKKyDC",
"object": "charge",
"created": 1540187487,
"livemode": true,
"paid": false,
"refunded": false,
"reversed": false,
"app": "app_QSqrT0jPKabD1qjz",
"channel": "ccb_qr",
"order_no": "912111810221540187487",
"client_ip": "180.168.5.158",
"amount": 10,
"amount_settle": 10,
"currency": "cny",
"subject": "ping++测试订单1540187487",
"body": "ping++订单内容1540187487",
"extra": {
"pos_id": "025547632",
"remark": "test",
"return_type": "1"
},
"time_paid": null,
"time_expire": 1540273887,
"time_settle": null,
"transaction_no": null,
"refunds": {
"object": "list",
"url": "/v1/charges/ch_bbnT0G44O088Hyzn94KKKyDC/refunds",
"has_more": false,
"data": []
},
"amount_refunded": 0,
"failure_code": null,
"failure_msg": null,
"metadata": {},
"credential": {
"object": "credential",
"ccb_qr": "9980001905035937882606"
},
"description": "Your description"
}
isv_qr
{
"id": "ch_Tm9OCGePqf9G148iTGPOyz50",
"object": "charge",
"created": 1495723380,
"livemode": true,
"paid": false,
"refunded": false,
"reversed": false,
"app": "app_1Gqj58ynP0mHeX1q",
"channel": "isv_qr",
"order_no": "149572338024366",
"client_ip": "127.0.0.1",
"amount": 100,
"amount_settle": 100,
"currency": "cny",
"subject": "Your Subject",
"body": "Your Body",
"extra": {
"pay_channel": "wx",
"terminal_id": "T0000001",
"buyer_account": "omYJse9x8w2TzmrxnUtrfr7qWhwc",
"channel_discount": 0,
"merchant_discount": 0
},
"time_paid": null,
"time_expire": 1495809780,
"time_settle": null,
"transaction_no": null,
"refunds": {
"object": "list",
"url": "/v1/charges/ch_Tm9OCGePqf9G148iTGPOyz50/refunds",
"has_more": false,
"data": []
},
"amount_refunded": 0,
"failure_code": null,
"failure_msg": null,
"metadata": {},
"credential": {
"object": "credential",
"isv_qr": "weixin://wxpay/bizpayurl?pr=ugRxcvq"
},
"description": null
}
isv_wap
{
"id": "ch_Xf5mbDPO0Kq94ejT4G8KCmDS",
"object": "charge",
"created": 1495784250,
"livemode": true,
"paid": false,
"refunded": false,
"reversed": false,
"app": "app_1Gqj58ynP0mHeX1q",
"channel": "isv_wap",
"order_no": "149578424976090",
"client_ip": "127.0.0.1",
"amount": 100,
"amount_settle": 100,
"currency": "cny",
"subject": "Your Subject",
"body": "Your Body",
"extra": {
"pay_channel": "alipay",
"terminal_id": "T0000001",
"result_url": "https://www.example.com/payment-result"
},
"time_paid": null,
"time_expire": 1495870650,
"time_settle": null,
"transaction_no": null,
"refunds": {
"object": "list",
"url": "/v1/charges/ch_Xf5mbDPO0Kq94ejT4G8KCmDS/refunds",
"has_more": false,
"data": []
},
"amount_refunded": 0,
"failure_code": null,
"failure_msg": null,
"metadata": {},
"credential": {
"object": "credential",
"isv_wap": "https://api.shou.money/scanpay/unified?data=eyJ2ZXJzaW9uIjoiMi4xIiwic2lnblR5cGUiOiJTSEEyNTYiLCJjaGFyc2V0IjoidXRmLTgiLCJvcmRlck51bSI6IjE0OTU3ODQyNDk3NjA5MCIsImJ1c2ljZCI6IldQQVkiLCJjaGNkIjoiQUxQIiwibWNobnRpZCI6IjIwODI3MDA0NzEwMDAwMyIsInRlcm1pbmFsaWQiOiIxMiIsInR4YW10IjoiMDAwMDAwMDAwMDAxIiwiYmFja1VybCI6Imh0dHBzOlwvXC9ub3RpZnkucGluZ3h4LmNvbVwvbm90aWZ5XC9jaGFyZ2VzXC9jaF9YZjVtYkRQTzBLcTk0ZWpUNEc4S0NtRFMiLCJmcm9udFVybCI6Imh0dHBzOlwvXC93d3cuZXhhbXBsZS5jb21cL3BheW1lbnQtcmVzdWx0IiwiYXR0YWNoIjoiY2hfWGY1bWJEUE8wS3E5NGVqVDRHOEtDbURTIiwic3ViamVjdCI6IllvdXIgU3ViamVjdCIsInNpZ24iOiI0NWE3YzU0Yzg0ZjBjYmRlODdmNmMyZjZkMjIxMDU4ZmYxMTJhNDE5YzU4OGY0ZmFjYmQwYjdiNWRhMTUzZThlIn0%3D"
},
"description": null
}
isv_scan
{
"id": "ch_injTG4KGWPePS88WPGPyfzz5",
"object": "charge",
"created": 1495694197,
"livemode": true,
"paid": true,
"refunded": false,
"reversed": false,
"app": "app_1Gqj58ynP0mHeX1q",
"channel": "isv_scan",
"order_no": "149569419725834",
"client_ip": "127.0.0.1",
"amount": 100,
"amount_settle": 100,
"currency": "cny",
"subject": "Your Subject",
"body": "Your Body",
"extra": {
"scan_code": "131141564583769183",
"terminal_id": "T0000001",
"pay_channel": "wx",
"buyer_account": "omYJse9x8w2TzmrxnUtrfr7qWhwc",
"channel_discount": 0,
"merchant_discount": 0
},
"time_paid": 1495694546,
"time_expire": 1495780597,
"time_settle": null,
"transaction_no": "4002362001201705252516756579",
"refunds": {
"object": "list",
"url": "/v1/charges/ch_injTG4KGWPePS88WPGPyfzz5/refunds",
"has_more": false,
"data": []
},
"amount_refunded": 0,
"failure_code": null,
"failure_msg": null,
"metadata": {},
"credential": {},
"description": null
}
alipay_qr

第四步:自行生成二维码并显示在 Client 端

在 Charge 对象中有 credential 字段,该字段中包含可以生成二维码的链接。你需要截取出链接并自行生成二维码,显示在你的 PC 端或任意你需要展示二维码的平台。

注意不要 将服务端获取的 Charge / Order / Recharge 传给客户端。

wx_pub_qr

第四步:自行生成二维码并显示在 Client 端

在 Charge 对象中有 credential 字段,该字段中包含可以生成二维码的链接。你需要截取出链接并自行生成二维码,显示在你的 PC 端或任意你需要展示二维码的平台。

注意不要 将服务端获取的 Charge / Order / Recharge 传给客户端。

cb_alipay_qr

第四步:自行生成二维码并显示在 Client 端

在 Charge 对象中有 credential 字段,该字段中包含可以生成二维码的链接。你需要截取出链接并自行生成二维码,显示在你的 PC 端或任意你需要展示二维码的平台。

注意不要 将服务端获取的 Charge / Order / Recharge 传给客户端。

cb_wx_pub_qr

第四步:自行生成二维码并显示在 Client 端

在 Charge 对象中有 credential 字段,该字段中包含可以生成二维码的链接。你需要截取出链接并自行生成二维码,显示在你的 PC 端或任意你需要展示二维码的平台。

注意不要 将服务端获取的 Charge / Order / Recharge 传给客户端。

upacp_qr

第四步:自行生成二维码并显示在 Client 端

在 Charge 对象中有 credential 字段,该字段中包含可以生成二维码的链接。你需要截取出链接并自行生成二维码,显示在你的 PC 端或任意你需要展示二维码的平台。

注意不要 将服务端获取的 Charge / Order / Recharge 传给客户端。

ccb_qr

第四步:自行生成二维码并显示在 Client 端

在 Charge 对象中有 credential 字段,该字段中包含可以生成二维码的链接。你需要截取出链接并自行生成二维码,显示在你的 PC 端或任意你需要展示二维码的平台。

注意不要 将服务端获取的 Charge / Order / Recharge 传给客户端。

isv_qr

第四步:自行生成二维码并显示在 Client 端

在 Charge 对象中有 credential 字段,该字段中包含可以生成二维码的链接。你需要截取出链接并自行生成二维码,显示在你的 PC 端或任意你需要展示二维码的平台。

注意不要 将服务端获取的 Charge / Order / Recharge 传给客户端。

isv_wap

第四步:将获得的 Charge 传给 Client

使用 isv_wap 渠道,你的服务器需要按照 JSON 字符串格式将支付凭据返回给你的客户端,Ping++ SDK 对此做了相应的处理,你只需要将获得的支付凭据 Charge 对象直接传给客户端(服务端应传递怎样的支付凭证给前端),客户端接收后使用该支付凭据用于调起支付控件,而支付凭据的传送方式需要你自行实现。

isv_scan

第四步:无需返回 Charge 给客户端

使用 scan 类渠道(包括alipay_scan、wx_pub_scan、cb_alipay_scan、cb_wx_pub_scan、upacp_scan、isv_scan),无需将此 Charge 传给客户端。在服务端请求 Ping++ 创建 Charge 后,若用户支付成功,Ping++ 会直接将带有支付结果的 Charge 对象返回给你的服务端,详细接入流程请参考 用户被扫

第五步:接收 Webhooks 通知

当用户完成交易后 Ping++ 会给你配置在 Ping++ 管理平台的 Webhooks 通知地址主动发送支付结果,我们称之为 Webhooks 通知。 Webhooks 通知是以 POST 形式发送的 JSON,放在请求的 body 里,内容是 Event 对象,支付成功的事件类型为 charge.succeeded ,你需要监听并接收 Webhooks 通知,接收到 Webhooks 后需要返回服务器状态码 2xx 表示接收成功,否则请返回状态码 500

$event = json_decode(file_get_contents("php://input"));
// 对异步通知做处理
if (!isset($event->type)) {
header($_SERVER['SERVER_PROTOCOL'] . ' 400 Bad Request');
exit("fail");
}
switch ($event->type) {
case "charge.succeeded":
// 开发者在此处加入对支付异步通知的处理代码
header($_SERVER['SERVER_PROTOCOL'] . ' 200 OK');
break;
case "refund.succeeded":
// 开发者在此处加入对退款异步通知的处理代码
header($_SERVER['SERVER_PROTOCOL'] . ' 200 OK');
break;
default:
header($_SERVER['SERVER_PROTOCOL'] . ' 400 Bad Request');
break;
}
import com.pingplusplus.model.Event;
import com.pingplusplus.model.Webhooks;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
public class ServletDemo extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("UTF8");
//获取头部所有信息
Enumeration headerNames = request.getHeaderNames();
while (headerNames.hasMoreElements()) {
String key = (String) headerNames.nextElement();
String value = request.getHeader(key);
System.out.println(key+" "+value);
}
// 获得 http body 内容
BufferedReader reader = request.getReader();
StringBuffer buffer = new StringBuffer();
String string;
while ((string = reader.readLine()) != null) {
buffer.append(string);
}
reader.close();
// 解析异步通知数据
Event event = Webhooks.eventParse(buffer.toString());
if ("charge.succeeded".equals(event.getType())) {
response.setStatus(200);
} else if ("refund.succeeded".equals(event.getType())) {
response.setStatus(200);
} else {
response.setStatus(500);
}
}
}
var http = require('http');
http.createServer(function (req, res) {
req.setEncoding('utf8');
var postData = "";
req.addListener("data", function (chunk) {
postData += chunk;
});
req.addListener("end", function () {
var resp = function (ret, status_code) {
res.writeHead(status_code, {
"Content-Type": "text/plain; charset=utf-8"
});
res.end(ret);
}
try {
var event = JSON.parse(postData);
if (event.type === undefined) {
return resp('Event 对象中缺少 type 字段', 400);
}
switch (event.type) {
case "charge.succeeded":
// 开发者在此处加入对支付异步通知的处理代码
return resp("OK", 200);
break;
case "refund.succeeded":
// 开发者在此处加入对退款异步通知的处理代码
return resp("OK", 200);
break;
default:
return resp("未知 Event 类型", 400);
break;
}
} catch (err) {
return resp('JSON 解析失败', 400);
}
});
}).listen(8080, "0.0.0.0");
import json
from flask import Flask, request, Response
# 使用 flask
@app.route('/webhooks', methods=['POST'])
def webhooks():
event = request.get_json()
if event['type'] == 'charge.succeeded':
return Response(status=200)
elif event['type'] == 'refund.succeeded':
return Response(status=200)
return Response(status=500)
if __name__ == '__main__':
app.run(debug=False, host='0.0.0.0', port=8080)
require 'webrick'
require 'json'
class Webhooks < WEBrick::HTTPServlet::AbstractServlet
def do_POST(request, response)
status = 400
response_body = '' # 可自定义
begin
event = JSON.parse(request.body)
if event['type'].nil?
response_body = 'Event 对象中缺少 type 字段'
elsif event['type'] == 'charge.succeeded'
# 开发者在此处加入对支付异步通知的处理代码
status = 200
response_body = 'OK'
elsif event['type'] == 'refund.succeeded'
# 开发者在此处加入对退款异步通知的处理代码
status = 200
response_body = 'OK'
else
response_body = '未知 Event 类型'
end
rescue JSON::ParserError
response_body = 'JSON 解析失败'
end
response.status = status
response['Content-Type'] = 'text/plain; charset=utf-8'
response.body = response_body
end
end
server = WEBrick::HTTPServer.new(:Port => 8000)
server.mount '/webhooks', Webhooks
trap 'INT' do server.shutdown end
server.start
func webhook(w http.ResponseWriter, r *http.Request) {
if strings.ToUpper(r.Method) == "POST" {
buf := new(bytes.Buffer)
buf.ReadFrom(r.Body)
signature := r.Header.Get("x-pingplusplus-signature")
webhook, err := pingpp.ParseWebhooks(buf.Bytes())
fmt.Println(webhook.Type)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
fmt.Fprintf(w, "fail")
return
}
if webhook.Type == "charge.succeeded" {
// TODO your code for charge
w.WriteHeader(http.StatusOK)
} else if webhook.Type == "refund.succeeded" {
// TODO your code for refund
w.WriteHeader(http.StatusOK)
} else {
w.WriteHeader(http.StatusInternalServerError)
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Pingpp.Models;
using System.IO;
namespace Example.Example
{
public class WebhooksDemo
{
public static Event Example()
{
var data = ReadFileToString(@"../../data.txt");
var evt = Webhooks.ParseWebhook(data);
Console.WriteLine(evt);
return evt;
}
public static string ReadFileToString(string path)
{
using (var sr = new StreamReader(path))
{
return sr.ReadToEnd();
}
}
}
}

以下是 Webhooks 通知地址配置的 charge.succeeded 对象的示例:

alipay_qr
{
"id": "evt_4Y1aP31wJYE07CpxIaIrNJWy",
"created": 1458736932,
"livemode": true,
"type": "charge.succeeded",
"data": {
"object": {
"id": "ch_Tiv5K0y98GCK0eP8e9j1O8S4",
"object": "charge",
"created": 1458736905,
"livemode": true,
"paid": true,
"refunded": false,
"app": "app_1Gqj58ynP0mHeX1q",
"channel": "alipay_qr",
"order_no": "123456789",
"client_ip": "116.228.208.114",
"amount": 100,
"amount_settle": 100,
"currency": "cny",
"subject": "Your Subject",
"body": "Your Body.",
"extra": {
"buyer_account": "xin***@126.com"
},
"time_paid": 1458736930,
"time_expire": 1458823305,
"time_settle": null,
"transaction_no": "2016032321001004770281868617",
"refunds": {
"object": "list",
"url": "/v1/charges/ch_Tiv5K0y98GCK0eP8e9j1O8S4/refunds",
"has_more": false,
"data": []
},
"amount_refunded": 0,
"failure_code": null,
"failure_msg": null,
"metadata": {
"keyyyy": "valueeeee"
},
"credential": {},
"description": "Your Description"
}
},
"object": "event",
"pending_webhooks": 78,
"request": "iar_qz5G0G0ajvL8fnX9uTvTKm94"
}
wx_pub_qr
{
"id": "evt_PX3bJCMaRaENMif4nxniXvTu",
"created": 1458736973,
"livemode": true,
"type": "charge.succeeded",
"data": {
"object": {
"id": "ch_0e5eHSiLaLGCCmrfnHLebjbH",
"object": "charge",
"created": 1458736958,
"livemode": true,
"paid": true,
"refunded": false,
"app": "app_1Gqj58ynP0mHeX1q",
"channel": "wx_pub_qr",
"order_no": "123456789",
"client_ip": "116.228.208.114",
"amount": 100,
"amount_settle": 100,
"currency": "cny",
"subject": "Your Subject",
"body": "Your Body.",
"extra": {
"product_id": "testlab1pingxx",
"open_id": "o9zpMs7Xk9e9aJbTXgufovuWGp7c",
"bank_type": "CFT"
},
"time_paid": 1458736973,
"time_expire": 1458744158,
"time_settle": null,
"transaction_no": "4001682001201603234220187331",
"refunds": {
"object": "list",
"url": "/v1/charges/ch_0e5eHSiLaLGCCmrfnHLebjbH/refunds",
"has_more": false,
"data": []
},
"amount_refunded": 0,
"failure_code": null,
"failure_msg": null,
"metadata": {
"keyyyy": "valueeeee"
},
"credential": {},
"description": "Your Description"
}
},
"object": "event",
"pending_webhooks": 92,
"request": "iar_XPyT44i9GiH8jLCeX95WzP4K"
}
cb_alipay_qr
{
"id": "evt_400181024164724000448901",
"created": 1540370843,
"livemode": false,
"type": "charge.succeeded",
"data": {
"object": {
"id": "ch_0azrD0qjXnv9nXLC0Surn1SK",
"object": "charge",
"created": 1539068132,
"livemode": true,
"paid": true,
"refunded": false,
"reversed": false,
"app": "app_1Gqj58ynP0mHeX1q",
"channel": "cb_alipay_qr",
"order_no": "20181009100002",
"client_ip": "127.0.0.1",
"amount": 10,
"amount_settle": 0,
"currency": "usd",
"subject": "IPhone 7 Plus",
"body": "测试订单内容-20181009100002",
"extra": {
"quantity": 1,
"price": "10",
"goods_list": [{
"goods_id": "cb_alipay_qr iphone8s16G",
"goods_name": "iPhone8s 256G",
"quantity": 1,
"price": "150",
"goods_category": "123456",
"body": "alipay_scan 之苹果手机16G",
"show_url": "https://www.pingxx.com/abc?1=a|b=goods_list"
}],
"show_url": "https://www.pingxx.com/abc?1=a\u0026b=2|c=3",
"sys_service_provider_id": "69195294",
"buyer_account": "alipay_account",
"buyer_user_id": "2088202084460157",
"rate": "6.64640000"
},
"time_paid": 1539068140,
"time_expire": 1539068312,
"time_settle": null,
"transaction_no": null,
"refunds": {
"object": "list",
"url": "/v1/charges/ch_0azrD0qjXnv9nXLC0Surn1SK/refunds",
"has_more": false,
"data": []
},
"amount_refunded": 0,
"failure_code": null,
"failure_msg": null,
"metadata": {},
"credential": {},
"description": null
}
},
"object": "event",
"request": "iar_yrfjfTezHmj9Lu9afLGmXrD0",
"pending_webhooks": 0
}
cb_wx_pub_qr
{
"id": "evt_PX3bJCMaRaENMif4nxniXvTu",
"created": 1458736973,
"livemode": true,
"type": "charge.succeeded",
"data": {
"object": {
"id": "ch_0e5eHSiLaLGCCmrfnHLebjbH",
"object": "charge",
"created": 1458736958,
"livemode": true,
"paid": true,
"refunded": false,
"app": "app_1Gqj58ynP0mHeX1q",
"channel": "cb_wx_pub_qr",
"order_no": "123456789",
"client_ip": "116.228.208.114",
"amount": 100,
"amount_settle": 100,
"currency": "usd",
"subject": "Your Subject",
"body": "Your Body.",
"extra": {
"product_id": "testlab1pingxx",
"goods_list": [
{
"goods_name": "iPhone",
"goods_num": "1"
}
],
"limit_pay": "no_credit",
"bank_type": "your bank type",
"is_subscribe": "N",
"cash_fee": 1,
"cash_fee_type": "cny",
"rate": 100000000
},
"time_paid": 1458736973,
"time_expire": 1458744158,
"time_settle": null,
"transaction_no": "4001682001201603234220187331",
"refunds": {
"object": "list",
"url": "/v1/charges/ch_0e5eHSiLaLGCCmrfnHLebjbH/refunds",
"has_more": false,
"data": []
},
"amount_refunded": 0,
"failure_code": null,
"failure_msg": null,
"metadata": {},
"credential": {},
"description": "Your Description"
}
},
"object": "event",
"pending_webhooks": 1,
"request": "iar_XPyT44i9GiH8jLCeX95WzP4K"
}
upacp_qr
{
"id": "evt_401181025100735000478801",
"created": 1540433255,
"livemode": true,
"type": "charge.succeeded",
"data": {
"object": {
"id": "ch_Ce1q1CDSaPaTz9efP0afH4aH",
"object": "charge",
"created": 1539237945,
"livemode": true,
"paid": true,
"refunded": false,
"reversed": false,
"app": "app_1Gqj58ynP0mHeX1q",
"channel": "upacp_qr",
"order_no": "201801009425",
"client_ip": "180.168.5.158",
"amount": 1,
"amount_settle": -9,
"currency": "cny",
"subject": "简米测试订单478",
"body": "订单内容",
"extra": {
"result_url": "https://www.pingxx.com"
},
"time_paid": 1539237950,
"time_expire": 1539324345,
"time_settle": null,
"transaction_no": null,
"refunds": {
"object": "list",
"url": "/v1/charges/ch_Ce1q1CDSaPaTz9efP0afH4aH/refunds",
"has_more": false,
"data": []
},
"amount_refunded": 0,
"failure_code": null,
"failure_msg": null,
"metadata": {},
"credential": {},
"description": null
}
},
"object": "event",
"request": "iar_vjr1CKvbDGO01OeXv5rzvzjD",
"pending_webhooks": 0
}
ccb_qr
{
"id": "evt_401181025100735000478801",
"created": 1540433255,
"livemode": true,
"type": "charge.succeeded",
"data": {
"object": {
"id": "ch_iXT4e5SmXrL00ifP0CSO4mv9",
"object": "charge",
"created": 1540812494,
"livemode": true,
"paid": true,
"refunded": false,
"reversed": false,
"app": "app_QSqrT0jPKabD1qjz",
"channel": "ccb_qr",
"order_no": "9121166050026542943",
"client_ip": "180.168.5.158",
"amount": 10,
"amount_settle": 10,
"currency": "cny",
"subject": "Ping++测试subjectymYFsUWSfYhlcvS",
"body": "Ping++测试bodyeVhkzHOZBOMmOjHtkPNBPfPMoexCBBwlmACQGSPQmBzIuoyHegrRXdFeRuYvBAWIAxPCPpzBvlcy",
"extra": {
"pos_id": "025547632",
"remark": "PingTest",
"return_type": "0"
},
"time_paid": 1540812693,
"time_expire": 1540898894,
"time_settle": null,
"transaction_no": null,
"refunds": {
"object": "list",
"url": "/v1/charges/ch_iXT4e5SmXrL00ifP0CSO4mv9/refunds",
"has_more": false,
"data": []
},
"amount_refunded": 0,
"failure_code": null,
"failure_msg": null,
"metadata": {
"test": "Ping++测试 Test"
},
"credential": {},
"description": "Ping++测试iLgsHcyDPuYwmxbOtZIjmeTvhsjOjbtBRswlgRDdxcLidksBTSiGspxrRkjidJaBYzCOxnIHIFfqIqMHKdgkfNLtLFBaPxRtnKBLnZDaBNImyxnBOVEuKQRlWNNYSecqvhPggiCTCIvBTQqElKxUlNtlShcZbvsxvMdtSNLSHCMrfqvGLFsDPGWhhUxEDSKWRjvpkkQAaWINxJlUevkkKcZXQGkRavmgReiEQovRToMjokEUublAv"
}
},
"object": "event",
"request": "iar_vjr1CKvbDGO01OeXv5rzvzjD",
"pending_webhooks": 0
}
isv_qr
{
"id": "evt_PX3bJCMaRaENMif4nxniXvTu",
"created": 1495723400,
"livemode": true,
"type": "charge.succeeded",
"data": {
"object": {
"id": "ch_Tm9OCGePqf9G148iTGPOyz50",
"object": "charge",
"created": 1495723380,
"livemode": true,
"paid": true,
"refunded": false,
"reversed": false,
"app": "app_1Gqj58ynP0mHeX1q",
"channel": "isv_qr",
"order_no": "149572338024366",
"client_ip": "127.0.0.1",
"amount": 100,
"amount_settle": 100,
"currency": "cny",
"subject": "Your Subject",
"body": "Your Body",
"extra": {
"pay_channel": "wx",
"terminal_id": "T0000001",
"buyer_account": "omYJse9x8w2TzmrxnUtrfr7qWhwc",
"channel_discount": 0,
"merchant_discount": 0
},
"time_paid": 1495723400,
"time_expire": 1495809780,
"time_settle": null,
"transaction_no": null,
"refunds": {
"object": "list",
"url": "/v1/charges/ch_Tm9OCGePqf9G148iTGPOyz50/refunds",
"has_more": false,
"data": []
},
"amount_refunded": 0,
"failure_code": null,
"failure_msg": null,
"metadata": {},
"credential": {
"object": "credential",
"isv_qr": "weixin://wxpay/bizpayurl?pr=ugRxcvq"
},
"description": null
}
},
"object": "event",
"pending_webhooks": 92,
"request": "iar_XPyT44i9GiH8jLCeX95WzP4K"
}
isv_wap
{
"id": "evt_YGyc34nD7X5PcjsPffocha3S",
"object": "event",
"type": "charge.succeeded",
"livemode": true,
"created": 1476853028,
"data": {
"object": {
"id": "ch_Xf5mbDPO0Kq94ejT4G8KCmDS",
"object": "charge",
"created": 1495784250,
"livemode": true,
"paid": true,
"refunded": false,
"reversed": false,
"app": "app_1Gqj58ynP0mHeX1q",
"channel": "isv_wap",
"order_no": "149578424976090",
"client_ip": "127.0.0.1",
"amount": 100,
"amount_settle": 100,
"currency": "cny",
"subject": "Your Subject",
"body": "Your Body",
"extra": {
"pay_channel": "alipay",
"terminal_id": "T0000001",
"result_url": "https://www.example.com/payment-result"
},
"time_paid": 1495784260,
"time_expire": 1495870650,
"time_settle": null,
"transaction_no": null,
"refunds": {
"object": "list",
"url": "/v1/charges/ch_Xf5mbDPO0Kq94ejT4G8KCmDS/refunds",
"has_more": false,
"data": []
},
"amount_refunded": 0,
"failure_code": null,
"failure_msg": null,
"metadata": {},
"credential": {
"object": "credential",
"isv_wap": "https://api.shou.money/scanpay/unified?data=eyJ2ZXJzaW9uIjoiMi4xIiwic2lnblR5cGUiOiJTSEEyNTYiLCJjaGFyc2V0IjoidXRmLTgiLCJvcmRlck51bSI6IjE0OTU3ODQyNDk3NjA5MCIsImJ1c2ljZCI6IldQQVkiLCJjaGNkIjoiQUxQIiwibWNobnRpZCI6IjIwODI3MDA0NzEwMDAwMyIsInRlcm1pbmFsaWQiOiIxMiIsInR4YW10IjoiMDAwMDAwMDAwMDAxIiwiYmFja1VybCI6Imh0dHBzOlwvXC9ub3RpZnkucGluZ3h4LmNvbVwvbm90aWZ5XC9jaGFyZ2VzXC9jaF9YZjVtYkRQTzBLcTk0ZWpUNEc4S0NtRFMiLCJmcm9udFVybCI6Imh0dHBzOlwvXC93d3cuZXhhbXBsZS5jb21cL3BheW1lbnQtcmVzdWx0IiwiYXR0YWNoIjoiY2hfWGY1bWJEUE8wS3E5NGVqVDRHOEtDbURTIiwic3ViamVjdCI6IllvdXIgU3ViamVjdCIsInNpZ24iOiI0NWE3YzU0Yzg0ZjBjYmRlODdmNmMyZjZkMjIxMDU4ZmYxMTJhNDE5YzU4OGY0ZmFjYmQwYjdiNWRhMTUzZThlIn0%3D"
},
"description": null
}
},
"pending_webhooks": 4,
"request": "iar_jT4eTKW9mPKKXfXbX15ujH44"
}
isv_scan
{
"id": "evt_YGyc34nD7X5PcjsPffocha3S",
"object": "event",
"type": "charge.succeeded",
"livemode": true,
"created": 1495694197,
"data": {
"object": {
"id": "ch_injTG4KGWPePS88WPGPyfzz5",
"object": "charge",
"created": 1495694197,
"livemode": true,
"paid": true,
"refunded": false,
"reversed": false,
"app": "app_1Gqj58ynP0mHeX1q",
"channel": "isv_scan",
"order_no": "149569419725834",
"client_ip": "127.0.0.1",
"amount": 100,
"amount_settle": 100,
"currency": "cny",
"subject": "Your Subject",
"body": "Your Body",
"extra": {
"scan_code": "131141564583769183",
"terminal_id": "T0000001",
"pay_channel": "wx",
"buyer_account": "omYJse9x8w2TzmrxnUtrfr7qWhwc",
"channel_discount": 0,
"merchant_discount": 0
},
"time_paid": 1495694546,
"time_expire": 1495780597,
"time_settle": null,
"transaction_no": "4002362001201705252516756579",
"refunds": {
"object": "list",
"url": "/v1/charges/ch_injTG4KGWPePS88WPGPyfzz5/refunds",
"has_more": false,
"data": []
},
"amount_refunded": 0,
"failure_code": null,
"failure_msg": null,
"metadata": {},
"credential": {},
"description": null
}
},
"pending_webhooks": 4,
"request": "iar_jT4eTKW9mPKKXfXbX15ujH44"
}

第六步:验证 Webhooks 签名

签名简介

Ping++ 的 Webhooks 通知包含了签名字段,可以使用该签名验证 Webhooks 通知的合法性。签名放置在 header 的自定义字段 x-pingplusplus-signature 中,签名用 RSA 私钥对 Webhooks 通知使用 RSA-SHA256 算法进行签名,以 base64 格式输出。

验证签名

Ping++ 在管理平台中提供了 RSA 公钥,供验证签名,该公钥具体获取路径:点击管理平台右上角公司名称->开发信息-> Ping++ 公钥。验证签名需要以下几步:

  1. 从 header 取出签名字段并对其进行 base64 解码。
  2. 获取 Webhooks 请求的原始数据。
  3. 将获取到的 Webhooks 通知、 Ping++ 管理平台提供的 RSA 公钥、和 base64 解码后的签名三者一同放入 RSA 的签名函数中进行非对称的签名运算,来判断签名是否验证通过。 Ping++ 提供了验证签名的 Demo Demo Demo Demo Demo Demo Demo , 放在 SDK 的 example 里供参考,我们在此不再赘述。

扫码支付查询

Ping++ 管理平台提供详细的订单信息和 Webhooks 功能,但是如果商户本身由于某种原因导致 Webhooks 没有收到或者延缓更新时,可以主动调用支付查询接口来获得交易的状态。

单笔支付查询

\Pingpp\Charge::retrieve('ch_id');
Charge ch = Charge.retrieve("ch_id");
pingpp.charges.retrieve(
"ch_id",
function(err, charge) {
// YOUR CODE
}
);
ch = pingpp.Charge.retrieve('ch_id')
Pingpp::Charge.retrieve("ch_id")
ch, err := charge.Get("ch_id")
var ch = Charge.Retrieve("ch_id");

支付列表查询

\Pingpp\Charge::all(array('limit' => 3));
Map chargeParams = new HashMap<String, Object>();
chargeParams.put("limit", 3);
Charge.all(chargeParams);
pingpp.charges.list({ limit: 3 }, function(err, charges) {
// YOUR CODE
});
res = pingpp.Charge.all(limit=3)
Pingpp::Charge.all(:limit => 3)
params := &pingpp.ChargeListParams{}
params.Filters.AddFilter("limit", "", "3")
//设置是不是只需要之前设置的 limit 这一个查询参数
params.Single = true
i := charge.List(params)
for i.Next() {
c := i.Charge()
ch, _ := json.Marshal(c)
fmt.Println(string(ch))
}
var chs = Charge.List(new Dictionary<string, object> {{"limit", 3}});

注意事项

  1. 扫码支付,客户端需要主动轮询服务端的支付结果来更新支付状态。isv_scan、isv_wap、isv_qr 渠道的订单建议增加间隔为5s的查询接口轮询,直到查询到支付成功应答或者超过一定时间,建议轮询总时长设置为60s,超过轮询时间后,调用取消接口关闭订单。
  2. 你需要在 Ping++ 的管理平台里填写 Webhooks 通知地址,详见 Webhooks 配置说明,你的服务器需要监听这个地址并且接收 Webhooks 通知,接收到 Webhooks 通知后需给 Ping++ 返回服务器状态 2xx 。此时事件类型是 charge.succeeded,其字段 data 包含了 object 字段, object 字段的值是一个 Charge 对象。
  3. 若你的服务器未正确返回 2xx,Ping++ 服务器会在 25 小时内向你的服务器不断重发通知,最多 10 次。Webhooks 首次是即时推送,重试通知时间间隔为 5s、10s、2min、5min、10min、30min、1h、2h、6h、15h,直到你正确回复状态 2xx 或者超过最大重发次数,Ping++ 将不再发送。
  4. 接收到 Webhooks 说明交易成功,交易失败不会发送 Webhooks 。
  5. 在可接受的时间范围内,如果你服务端没有收到 Webhooks 的通知,你也可以调用 Server-SDK 封装的查询方法,主动向 Ping++ 发起请求来获得订单状态,该查询结果可以作为交易结果。

下一步撤销