企业付款

接入 Ping++ 发起企业付款,仅需要 Ping++ Server SDK 即可。服务器端需要做的就是向 Ping++ 请求 Transfer 对象,并且监听和获取 Webhooks 通知,具体步骤如下:

  1. 设置 API-Key
  2. SDK 验证签名设置
  3. 服务端发起企业付款请求获取 Transfer 对象
  4. 接收 Webhooks 通知
  5. 验证 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 公钥和私钥? 注意:针对 微信小程序企业付款、银联电子代付、单笔转账到支付宝账户、通联代付、京东代付 ,必须要进行 SDK 签名验证。

设置请求签名密钥

你需要在代码中设置请求签名的私钥(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 开关。

第三步:服务端发起企业付款请求获取 Transfer 对象

调用 Ping++ Server SDK 发起转账请求,发起请求所需参数具体可参考 API 文档,目前支持微信 App、微信 JSAPI 及小程序企业付款接口、银联电子代付接口、通联代付接口、京东代付接口和支付宝企业付款接口。

wx
$tr = \Pingpp\Transfer::create(
array(
'amount' => 100,//订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
'order_no' => '123456789',
'currency' => 'cny',
'channel' => 'wx',//此处 wx 为开放平台的支付
'app' => array('id' => 'app_1Gqj58ynP0mHeX1q'),
'type' => 'b2c',
'recipient' => 'User OpenId',//企业付款给指定用户的 open_id
'description' => 'Your Description',
'extra' => array('user_name' => 'User Name', 'force_check' => false)
)
);
echo $tr;
Map<String, Object> transferMap = new HashMap<String, Object>();
transferMap.put("channel", "wx");//此处 wx 为开放平台的支付
transferMap.put("order_no", "123456789");
transferMap.put("amount", "100");//订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
transferMap.put("type", "b2c");
transferMap.put("currency", "cny");
transferMap.put("recipient", "User OpenId");//企业付款给指定用户的 open_id
transferMap.put("description", "Your Description");
Map<String, String> app = new HashMap<String, String>();
app.put("id", "app_1Gqj58ynP0mHeX1q");
transferMap.put("app", app);
Map<String, Object> extra = new HashMap<String, Object>();
extra.put("user_name", "User Name");
extra.put("force_check", false);
transferMap.put("extra", extra);
Transfer transfer = Transfer.create(transferMap);
pingpp.transfers.create({
order_no: "123456789",
app: { id: "app_1Gqj58ynP0mHeX1q" },
channel: "wx",//此处 wx 为开放平台的支付
amount: 100,//订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
currency: "cny",
type: "b2c",
recipient: "User OpenId",//企业付款给指定用户的 open_id
extra: {
user_name: "User Name",
force_check: false
},
description: "Your Description"
}, function(err, transfer) {
// YOUR CODE
});
tr = pingpp.Transfer.create(
order_no='123456789',
channel='wx',//此处 wx 为开放平台的支付
amount=100, #订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
currency='cny',
app=dict(id='app_1Gqj58ynP0mHeX1q'),
type='b2c',
recipient='User OpenId',//企业付款给指定用户的 open_id
extra=dict(user_name='User Name', force_check=false),
description='description'
)
print tr
transfer = Pingpp::Transfer.create(
:order_no => "123456789",
:app => { :id => "app_1Gqj58ynP0mHeX1q" },
:channel => "wx",# 此处 wx 为开放平台的支付
:amount => 100,# 订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
:currency => "cny",
:type => "b2c",
:recipient => "User OpenId",# 企业付款给指定用户的 open_id
:extra => {
:user_name => "User Name",
:force_check => false
},
:description => "Your Description"
)
puts transfer
extra := make(map[string]interface{})
extra["user_name"] = "user name"
extra["force_check"] = false
transferParams := &pingpp.TransferParams{
App: pingpp.App{Id: "app_1Gqj58ynP0mHeX1q"},
Channel: "wx",//此处 wx 为开放平台的支付
Order_no: "123456789",
Amount: 100,//订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
Currency: "cny",
Type: "b2c",
Recipient: "User OpenId",//企业付款给指定用户的 open_id
Description: "Your Description",
Extra: extra,
}
transfer, err := transfer.New(transferParams)
var trParams = new Dictionary<string, object>
{
{"order_no", "123456789"},
{"amount", 100},//订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
{"channel", "wx"},//此处 wx 为开放平台的支付
{"currency", "cny"},
{"type", "b2c"},
{"recipient", "User OpenId"},//企业付款给指定用户的 open_id
{"description", "Description"},
{"app", new Dictionary<string, string> {{"id", "app_1Gqj58ynP0mHeX1q"}}},
{
"extra", new Dictionary<string, object>
{
{"user_name", "User Name"},
{"force_check", false}
}
}
};
var tr = Transfer.Create(trParams);
wx_pub
$tr = \Pingpp\Transfer::create(
array(
'amount' => 100,//订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
'order_no' => '123456789',
'currency' => 'cny',
'channel' => 'wx_pub',//此处 wx_pub 为公众平台的支付
'app' => array('id' => 'app_1Gqj58ynP0mHeX1q'),
'type' => 'b2c',
'recipient' => 'User OpenId',//企业付款给指定用户的 open_id
'description' => 'Your Description',
'extra' => array('user_name' => 'User Name', 'force_check' => false)
)
);
echo $tr;
Map<String, Object> transferMap = new HashMap<String, Object>();
transferMap.put("channel", "wx_pub");//此处 wx_pub 为公众平台的支付
transferMap.put("order_no", "123456789");
transferMap.put("amount", "100");//订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
transferMap.put("type", "b2c");
transferMap.put("currency", "cny");
transferMap.put("recipient", "User OpenId");//企业付款给指定用户的 open_id
transferMap.put("description", "Your Description");
Map<String, String> app = new HashMap<String, String>();
app.put("id", "app_1Gqj58ynP0mHeX1q");
transferMap.put("app", app);
Map<String, Object> extra = new HashMap<String, Object>();
extra.put("user_name", "User Name");
extra.put("force_check", false);
transferMap.put("extra", extra);
Transfer transfer = Transfer.create(transferMap);
pingpp.transfers.create({
order_no: "123456789",
app: { id: "app_1Gqj58ynP0mHeX1q" },
channel: "wx_pub",//此处 wx_pub 为公众平台的支付
amount: 100,//订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
currency: "cny",
type: "b2c",
recipient: "User OpenId",//企业付款给指定用户的 open_id
extra: {
user_name: "User Name",
force_check: false
},
description: "Your Description"
}, function(err, transfer) {
// YOUR CODE
});
tr = pingpp.Transfer.create(
order_no='123456789',
channel='wx_pub',//此处 wx_pub 为公众平台的支付
amount=100, #订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
currency='cny',
app=dict(id='app_1Gqj58ynP0mHeX1q'),
type='b2c',
recipient='User OpenId',//企业付款给指定用户的 open_id
extra=dict(user_name='User Name', force_check=false),
description='description'
)
print tr
transfer = Pingpp::Transfer.create(
:order_no => "123456789",
:app => { :id => "app_1Gqj58ynP0mHeX1q" },
:channel => "wx_pub",# 此处 wx_pub 为公众平台的支付
:amount => 100,# 订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
:currency => "cny",
:type => "b2c",
:recipient => "User OpenId",# 企业付款给指定用户的 open_id
:extra => {
:user_name => "User Name",
:force_check => false
},
:description => "Your Description"
)
puts transfer
extra := make(map[string]interface{})
extra["user_name"] = "user name"
extra["force_check"] = false
transferParams := &pingpp.TransferParams{
App: pingpp.App{Id: "app_1Gqj58ynP0mHeX1q"},
Channel: "wx_pub",//此处 wx_pub 为公众平台的支付
Order_no: "123456789",
Amount: 100,//订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
Currency: "cny",
Type: "b2c",
Recipient: "User OpenId",//企业付款给指定用户的 open_id
Description: "Your Description",
Extra: extra,
}
transfer, err := transfer.New(transferParams)
var trParams = new Dictionary<string, object>
{
{"order_no", "123456789"},
{"amount", 100},//订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
{"channel", "wx_pub"},//此处 wx_pub 为公众平台的支付
{"currency", "cny"},
{"type", "b2c"},
{"recipient", "User OpenId"},//企业付款给指定用户的 open_id
{"description", "Description"},
{"app", new Dictionary<string, string> {{"id", "app_1Gqj58ynP0mHeX1q"}}},
{
"extra", new Dictionary<string, object>
{
{"user_name", "User Name"},
{"force_check", false}
}
}
};
var tr = Transfer.Create(trParams);
wx_lite
$tr = \Pingpp\Transfer::create(
array(
'amount' => 100,//订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
'order_no' => '123456789',
'currency' => 'cny',
'channel' => 'wx_lite', //此处 wx_lite 为公众平台的支付
'app' => array('id' => 'app_1Gqj58ynP0mHeX1q'),
'type' => 'b2c',
'recipient' => 'User OpenId',//企业付款给指定用户的 open_id
'description' => 'Your Description',
'extra' => array('user_name' => 'User Name', 'force_check' => false)
)
);
echo $tr;
Map<String, Object> transferMap = new HashMap<String, Object>();
transferMap.put("channel", "wx_lite");//此处 wx_lite 为公众平台的支付
transferMap.put("order_no", "123456789");
transferMap.put("amount", "100");//订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
transferMap.put("type", "b2c");
transferMap.put("currency", "cny");
transferMap.put("recipient", "User OpenId");//企业付款给指定用户的 open_id
transferMap.put("description", "Your Description");
Map<String, String> app = new HashMap<String, String>();
app.put("id", "app_1Gqj58ynP0mHeX1q");
transferMap.put("app", app);
Map<String, Object> extra = new HashMap<String, Object>();
extra.put("user_name", "User Name");
extra.put("force_check", false);
transferMap.put("extra", extra);
Transfer transfer = Transfer.create(transferMap);
pingpp.transfers.create({
order_no: "123456789",
app: { id: "app_1Gqj58ynP0mHeX1q" },
channel: "wx_lite",//此处 wx_lite 为公众平台的支付
amount: 100,//订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
currency: "cny",
type: "b2c",
recipient: "User OpenId",//企业付款给指定用户的 open_id
extra: {
user_name: "User Name",
force_check: false
},
description: "Your Description"
}, function(err, transfer) {
// YOUR CODE
});
tr = pingpp.Transfer.create(
order_no='123456789',
channel='wx_lite',//此处 wx_lite 为公众平台的支付
amount=100, #订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
currency='cny',
app=dict(id='app_1Gqj58ynP0mHeX1q'),
type='b2c',
recipient='User OpenId',//企业付款给指定用户的 open_id
extra=dict(user_name='User Name', force_check=false),
description='description'
)
print tr
transfer = Pingpp::Transfer.create(
:order_no => "123456789",
:app => { :id => "app_1Gqj58ynP0mHeX1q" },
:channel => "wx_lite",# 此处 wx_lite 为公众平台的支付
:amount => 100,# 订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
:currency => "cny",
:type => "b2c",
:recipient => "User OpenId",# 企业付款给指定用户的 open_id
:extra => {
:user_name => "User Name",
:force_check => false
},
:description => "Your Description"
)
puts transfer
extra := make(map[string]interface{})
extra["user_name"] = "user name"
extra["force_check"] = false
transferParams := &pingpp.TransferParams{
App: pingpp.App{Id: "app_1Gqj58ynP0mHeX1q"},
Channel: "wx_lite",//此处 wx_lite 为公众平台的支付
Order_no: "123456789",
Amount: 100,//订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
Currency: "cny",
Type: "b2c",
Recipient: "User OpenId",//企业付款给指定用户的 open_id
Description: "Your Description",
Extra: extra,
}
transfer, err := transfer.New(transferParams)
var trParams = new Dictionary<string, object>
{
{"order_no", "123456789"},
{"amount", 100},//订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
{"channel", "wx_lite"},//此处 wx_lite 为公众平台的支付
{"currency", "cny"},
{"type", "b2c"},
{"recipient", "User OpenId"},//企业付款给指定用户的 open_id
{"description", "Description"},
{"app", new Dictionary<string, string> {{"id", "app_1Gqj58ynP0mHeX1q"}}},
{
"extra", new Dictionary<string, object>
{
{"user_name", "User Name"},
{"force_check", false}
}
}
};
var tr = Transfer.Create(trParams);
unionpay
$tr = \Pingpp\Transfer::create(
array(
'amount' => 100,// 订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
'order_no' => '123456789',
'currency' => 'cny',
'channel' => 'unionpay',// 银联电子代付
'app' => array('id' => 'app_1Gqj58ynP0mHeX1q'),
'type' => 'b2c',
'description' => 'Your Description',
'extra' => array(
'card_number' => '6225220317083517',
'user_name' => 'User Name',
'open_bank_code' => '0102'
)
);
echo $tr;
Map<String, Object> transferMap = new HashMap<String, Object>();
transferMap.put("channel", "unionpay");// 银联电子代付
transferMap.put("order_no", "123456789");
transferMap.put("amount", "100");//订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
transferMap.put("type", "b2c");
transferMap.put("currency", "cny");
transferMap.put("description", "Your Description");
Map<String, String> app = new HashMap<String, String>();
app.put("id", "app_1Gqj58ynP0mHeX1q");
transferMap.put("app", app);
Map<String, Object> extra = new HashMap<String, Object>();
extra.put("card_number", "6225220317083517");
extra.put("user_name", "User Name");
extra.put("open_bank_code", "0102");
transferMap.put("extra", extra);
Transfer transfer = Transfer.create(transferMap);
pingpp.transfers.create({
order_no: "123456789",
app: { id: "app_1Gqj58ynP0mHeX1q" },
channel: "unionpay",// 银联电子代付
amount: 100,// 订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
currency: "cny",
type: "b2c",
extra: {
card_number: "6225220317083517",
user_name: "User Name",
open_bank_code: "0102"
},
description: "Your Description"
}, function(err, transfer) {
// YOUR CODE
});
tr = pingpp.Transfer.create(
order_no='123456789',
channel='unionpay',# 银联电子代付
amount=100, # 订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
currency='cny',
app=dict(id='app_1Gqj58ynP0mHeX1q'),
type='b2c',
extra=dict(
card_number='6225220317083517',
user_name='User Name',
open_bank_code='0102'
),
description='description'
)
print tr
transfer = Pingpp::Transfer.create(
:order_no => "123456789",
:app => { :id => "app_1Gqj58ynP0mHeX1q" },
:channel => "unionpay",# 银联电子代付
:amount => 100,# 订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
:currency => "cny",
:type => "b2c",
:extra => {
:card_number => "6225220317083517",
:user_name => "User Name",
:open_bank_code => "0102"
},
:description => "Your Description"
)
puts transfer
extra := make(map[string]interface{})
extra["card_number"] = "6225220317083517"
extra["user_name"] = "user name"
extra["open_bank_code"] = "0102"
transferParams := &pingpp.TransferParams{
App: pingpp.App{Id: "app_1Gqj58ynP0mHeX1q"},
Channel: "unionpay",// 银联电子代付
Order_no: "123456789",
Amount: 100,//订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
Currency: "cny",
Type: "b2c",
Description: "Your Description",
Extra: extra,
}
transfer, err := transfer.New(transferParams)
var trParams = new Dictionary<string, object>
{
{"order_no", "123456789"},
{"amount", 100},//订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
{"channel", "unionpay"},// 银联电子代付
{"currency", "cny"},
{"type", "b2c"},
{"description", "Description"},
{"app", new Dictionary<string, string> {{"id", "app_1Gqj58ynP0mHeX1q"}}},
{
"extra", new Dictionary<string, object>
{
{"card_number", "6225220317083517"},
{"user_name", "User Name"},
{"open_bank_code", "0102"}
}
}
};
var tr = Transfer.Create(trParams);
alipay
$tr = \Pingpp\Transfer::create(
array(
'amount' => 100,//订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
'order_no' => '123456789',
'currency' => 'cny',
'channel' => 'alipay',//单笔转账到支付宝账户
'app' => array('id' => 'app_1Gqj58ynP0mHeX1q'),
'type' => 'b2c',
'recipient' => 'User Account',//若 type 为 b2c,为个人支付宝账号,若 type 为 b2b,为企业支付宝账号。
'description' => 'Your Description',
'extra' => array('recipient_name' => 'Recipient Name', 'recipient_account_type' => 'Your Account Type')
)
);
echo $tr;
Map<String, Object> transferMap = new HashMap<String, Object>();
transferMap.put("channel", "alipay");//单笔转账到支付宝账户
transferMap.put("order_no", "123456789");
transferMap.put("amount", "100");//订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
transferMap.put("type", "b2c");
transferMap.put("currency", "cny");
transferMap.put("recipient", "User Account");//若 type 为 b2c,为个人支付宝账号,若 type 为 b2b,为企业支付宝账号。
transferMap.put("description", "Your Description");
Map<String, String> app = new HashMap<String, String>();
app.put("id", "app_1Gqj58ynP0mHeX1q");
transferMap.put("app", app);
Map<String, Object> extra = new HashMap<String, Object>();
extra.put("recipient_name", "Recipient Name");
extra.put("recipient_account_type", "Your Account Type");
transferMap.put("extra", extra);
Transfer transfer = Transfer.create(transferMap);
pingpp.transfers.create({
order_no: "123456789",
app: { id: "app_1Gqj58ynP0mHeX1q" },
channel: "alipay",//单笔转账到支付宝账户
amount: 100,//订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
currency: "cny",
type: "b2c",
recipient: "User Account",//若 type 为 b2c,为个人支付宝账号,若 type 为 b2b,为企业支付宝账号。
extra: {
recipient_name: "Recipient Name",
recipient_account_type: "Your Account Type"
},
description: "Your Description"
}, function(err, transfer) {
// YOUR CODE
});
tr = pingpp.Transfer.create(
order_no='123456789',
channel='alipay',//单笔转账到支付宝账户
amount=100, #订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
currency='cny',
app=dict(id='app_1Gqj58ynP0mHeX1q'),
type='b2c',
recipient='User Account',//若 type 为 b2c,为个人支付宝账号,若 type 为 b2b,为企业支付宝账号。
extra=dict(recipient_name ='Recipient Name', recipient_account_type = 'Your Account Type'),
description='description'
)
print tr
transfer = Pingpp::Transfer.create(
:order_no => "123456789",
:app => { :id => "app_1Gqj58ynP0mHeX1q" },
:channel => "alipay",# 单笔转账到支付宝账户
:amount => 100,# 订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
:currency => "cny",
:type => "b2c",
:recipient => "User Account",# 若 type 为 b2c,为个人支付宝账号,若 type 为 b2b,为企业支付宝账号。
:extra => {
: recipient_name => "Recipient Name",
: recipient_account_type => "Your Account Type"
},
:description => "Your Description"
)
puts transfer
extra := make(map[string]interface{})
extra["recipient_name"] = "Recipient Name"
extra["recipient_account_type"] = "Your Account Type"
transferParams := &pingpp.TransferParams{
App: pingpp.App{Id: "app_1Gqj58ynP0mHeX1q"},
Channel: "alipay",//单笔转账到支付宝账户
Order_no: "123456789",
Amount: 100,//订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
Currency: "cny",
Type: "b2c",
Recipient: "User Account",//若 type 为 b2c,为个人支付宝账号,若 type 为 b2b,为企业支付宝账号。
Description: "Your Description",
Extra: extra,
}
transfer, err := transfer.New(transferParams)
var trParams = new Dictionary<string, object>
{
{"order_no", "123456789"},
{"amount", 100},//订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
{"channel", "alipay"},//单笔转账到支付宝账户
{"currency", "cny"},
{"type", "b2c"},
{"recipient", "User Account"},//若 type 为 b2c,为个人支付宝账号,若 type 为 b2b,为企业支付宝账号。
{"description", "Description"},
{"app", new Dictionary<string, string> {{"id", "app_1Gqj58ynP0mHeX1q"}}},
{
"extra", new Dictionary<string, object>
{
{"recipient_name", "Recipient Name"},
{"recipient_account_type", "Your Account Type"}
}
}
};
var tr = Transfer.Create(trParams);
allinpay
$tr = \Pingpp\Transfer::create(
array(
'amount' => 100,//订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
'order_no' => '3099123456789148516002992218',
'currency' => 'cny',
'channel' => 'allinpay',//通联代付
'app' => array('id' => 'app_1Gqj58ynP0mHeX1q'),
'type' => 'b2c',
'description' => 'Your Description',
'extra' => array('card_number' => 'Card Number', 'user_name' => 'User Name', 'open_bank_code' => '0308')
)
);
echo $tr;
Map<String, Object> transferMap = new HashMap<String, Object>();
transferMap.put("channel", "allinpay");//通联代付
transferMap.put("order_no", "3099123456789148516002992218");
transferMap.put("amount", "100");//订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
transferMap.put("type", "b2c");
transferMap.put("currency", "cny");
transferMap.put("description", "Your Description");
Map<String, String> app = new HashMap<String, String>();
app.put("id", "app_1Gqj58ynP0mHeX1q");
transferMap.put("app", app);
Map<String, Object> extra = new HashMap<String, Object>();
extra.put("card_number", "Card Number");
extra.put("user_name", "User Name");
extra.put("open_bank_code", "0308");
transferMap.put("extra", extra);
Transfer transfer = Transfer.create(transferMap);
pingpp.transfers.create({
order_no: "3099123456789148516002992218",
app: { id: "app_1Gqj58ynP0mHeX1q" },
channel: "allinpay",//通联代付
amount: 100,//订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
currency: "cny",
type: "b2c",
extra: {
card_number: "Card Number",
user_name: "User Name",
open_bank_code: "0308"
},
description: "Your Description"
}, function(err, transfer) {
// YOUR CODE
});
tr = pingpp.Transfer.create(
order_no='3099123456789148516002992218',
channel='allinpay',//通联代付
amount=100, #订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
currency='cny',
app=dict(id='app_1Gqj58ynP0mHeX1q'),
type='b2c',
extra=dict(card_number = 'Card Number', user_name = 'User Name', open_bank_code = '0308'),
description='description'
)
print tr
transfer = Pingpp::Transfer.create(
:order_no => "3099123456789148516002992218",
:app => { :id => "app_1Gqj58ynP0mHeX1q" },
:channel => "allinpay",# 通联代付
:amount => 100,# 订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
:currency => "cny",
:type => "b2c",
:extra => {
: card_number => "Card Number",
: user_name => "User Name",
: open_bank_code => "0308"
},
:description => "Your Description"
)
puts transfer
extra := make(map[string]interface{})
extra["card_number"] = "Card Number"
extra["user_name"] = "User Name"
extra["open_bank_code"] = "0308"
transferParams := &pingpp.TransferParams{
App: pingpp.App{Id: "app_1Gqj58ynP0mHeX1q"},
Channel: "allinpay",//通联代付
Order_no: "3099123456789148516002992218",
Amount: 100,//订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
Currency: "cny",
Type: "b2c",
Description: "Your Description",
Extra: extra,
}
transfer, err := transfer.New(transferParams)
var trParams = new Dictionary<string, object>
{
{"order_no", "3099123456789148516002992218"},
{"amount", 100},//订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
{"channel", "allinpay"},//通联代付
{"currency", "cny"},
{"type", "b2c"},
{"description", "Description"},
{"app", new Dictionary<string, string> {{"id", "app_1Gqj58ynP0mHeX1q"}}},
{
"extra", new Dictionary<string, object>
{
{"card_number", "Card Number"},
{"user_name", "User Name"},
{"open_bank_code", "0308"}
}
}
};
var tr = Transfer.Create(trParams);
jdpay
$tr = \Pingpp\Transfer::create(
array(
'amount' => 100,//订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
'order_no' => '123456789',
'currency' => 'cny',
'channel' => 'jdpay',//京东代付
'app' => array('id' => 'app_1Gqj58ynP0mHeX1q'),
'type' => 'b2c',
'description' => 'Your Description',
'extra' => array('card_number' => 'Card Number', 'user_name' => 'User Name', 'open_bank_code' => '0100')
)
);
echo $tr;
Map<String, Object> transferMap = new HashMap<String, Object>();
transferMap.put("channel", "jdpay");//京东代付
transferMap.put("order_no", "123456789");
transferMap.put("amount", "100");//订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
transferMap.put("type", "b2c");
transferMap.put("currency", "cny");
transferMap.put("description", "Your Description");
Map<String, String> app = new HashMap<String, String>();
app.put("id", "app_1Gqj58ynP0mHeX1q");
transferMap.put("app", app);
Map<String, Object> extra = new HashMap<String, Object>();
extra.put("card_number", "Card Number");
extra.put("user_name", "User Name");
extra.put("open_bank_code", "0100");
transferMap.put("extra", extra);
Transfer transfer = Transfer.create(transferMap);
pingpp.transfers.create({
order_no: "123456789",
app: { id: "app_1Gqj58ynP0mHeX1q" },
channel: "jdpay",//京东代付
amount: 100,//订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
currency: "cny",
type: "b2c",
extra: {
card_number: "Card Number",
user_name: "User Name",
open_bank_code: "0100"
},
description: "Your Description"
}, function(err, transfer) {
// YOUR CODE
});
tr = pingpp.Transfer.create(
order_no='123456789',
channel='jdpay',//京东代付
amount=100, #订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
currency='cny',
app=dict(id='app_1Gqj58ynP0mHeX1q'),
type='b2c',
extra=dict(card_number = 'Card Number', user_name = 'User Name', open_bank_code = '0100'),
description='description'
)
print tr
transfer = Pingpp::Transfer.create(
:order_no => "123456789",
:app => { :id => "app_1Gqj58ynP0mHeX1q" },
:channel => "jdpay",# 京东代付
:amount => 100,# 订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
:currency => "cny",
:type => "b2c",
:extra => {
: card_number => "Card Number",
: user_name => "User Name",
: open_bank_code => "0100"
},
:description => "Your Description"
)
puts transfer
extra := make(map[string]interface{})
extra["card_number"] = "Card Number"
extra["user_name"] = "User Name"
extra["open_bank_code"] = "0100"
transferParams := &pingpp.TransferParams{
App: pingpp.App{Id: "app_1Gqj58ynP0mHeX1q"},
Channel: "jdpay",//京东代付
Order_no: "123456789",
Amount: 100,//订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
Currency: "cny",
Type: "b2c",
Description: "Your Description",
Extra: extra,
}
transfer, err := transfer.New(transferParams)
var trParams = new Dictionary<string, object>
{
{"order_no", "123456789"},
{"amount", 100},//订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100)
{"channel", "jdpay"},//京东代付
{"currency", "cny"},
{"type", "b2c"},
{"description", "Description"},
{"app", new Dictionary<string, string> {{"id", "app_1Gqj58ynP0mHeX1q"}}},
{
"extra", new Dictionary<string, object>
{
{"card_number", "Card Number"},
{"user_name", "User Name"},
{"open_bank_code", "0100"}
}
}
};
var tr = Transfer.Create(trParams);

Ping++ 收到转账请求后返回给你的服务器一个 Transfer 对象,下面是 Transfer 的一个示例:

wx
{
"id": "tr_irXvj5LG8CSGffTOG05S84K4",
"object": "transfer",
"type": "b2c",
"created": 1458191226,
"time_transferred": null,
"livemode": true,
"status": "pending",
"app": "app_1Gqj58ynP0mHeX1q",
"channel": "wx",
"order_no": "123456789",
"batch_no": null,
"amount": 100,
"amount_settle": 100,
"currency": "cny",
"recipient": "o9zpMs7Xk9e9aJbTXgufovuWG000",
"description": "Your Description",
"transaction_no": "1000018301201603170751176271",
"failure_msg": null,
"extra": {},
"metadata": {}
}
wx_pub
{
"id": "tr_irXvj5LG8CSGffTOG05S84K4",
"object": "transfer",
"type": "b2c",
"created": 1458191226,
"time_transferred": null,
"livemode": true,
"status": "pending",
"app": "app_1Gqj58ynP0mHeX1q",
"channel": "wx_pub",
"order_no": "123456789",
"batch_no": null,
"amount": 100,
"amount_settle": 100,
"currency": "cny",
"recipient": "o9zpMs7Xk9e9aJbTXgufovuWGp7B",
"description": "Your Description",
"transaction_no": "1000018301201603170751176271",
"failure_msg": null,
"extra": {},
"metadata": {}
}
wx_lite
{
"id": "tr_irXvj5LG8CSGffTOG05S84K4",
"object": "transfer",
"type": "b2c",
"created": 1458191226,
"time_transferred": null,
"livemode": true,
"status": "pending",
"app": "app_1Gqj58ynP0mHeX1q",
"channel": "wx_lite",
"order_no": "123456789",
"batch_no": null,
"amount": 100,
"amount_settle": 100,
"currency": "cny",
"recipient": "o9zpMs7Xk9e9aJbTXgufovuWGp7B",
"description": "Your Description",
"transaction_no": "1000018301201603170751176271",
"failure_msg": null,
"extra": {},
"metadata": {}
}
unionpay
{
"id": "tr_0eTi1OGqr9iH0i9CePf1a9C0",
"object": "transfer",
"type": "b2c",
"created": 1467615446,
"time_transferred": 1467615446,
"livemode": true,
"status": "scheduled",
"app": "app_1Gqj58ynP0mHeX1q",
"channel": "unionpay",
"order_no": "123456789",
"amount": 100,
"amount_settle": 100,
"currency": "cny",
"recipient": null,
"description": "Your Description",
"transaction_no": "371646",
"failure_msg": null,
"extra": {
"card_number": "6225220317083517",
"user_name": "张三",
"open_bank_code": "0102"
}
}
alipay
{
"id": "tr_irXvj5LG8CSGffTOG05S84K4",
"object": "transfer",
"type": "b2c",
"created": 1458191226,
"time_transferred": null,
"livemode": true,
"status": "pending",
"app": "app_1Gqj58ynP0mHeX1q",
"channel": "alipay",
"order_no": "123456789",
"batch_no": null,
"amount": 100,
"amount_settle": 100,
"currency": "cny",
"recipient": "13333333333",
"description": "Your Description",
"transaction_no": "2016032321001004920229421275",
"failure_msg": null,
"extra": {
"recipient_name": "张三丰",
"recipient_account_type": "13333333333"
}
}
allinpay
{
"id": "tr_irXvj5LG8CSGffTOG05S84K4",
"object": "transfer",
"type": "b2c",
"created": 1458191226,
"time_transferred": null,
"livemode": true,
"status": "pending",
"app": "app_1Gqj58ynP0mHeX1q",
"channel": "allinpay",
"order_no": "3099123456789148516002992218",
"batch_no": null,
"amount": 100,
"amount_settle": 100,
"currency": "cny",
"recipient": "",
"description": "Your Description",
"transaction_no": "3099123456789148516002992218",
"failure_msg": null,
"extra": {
"card_number": "621485022119802",
"user_name": "张三",
"open_bank_code": "0308"
}
}
jdpay
{
"id": "tr_irXvj5LG8CSGffTOG05S84K4",
"object": "transfer",
"type": "b2c",
"created": 1458191226,
"time_transferred": null,
"livemode": true,
"status": "pending",
"app": "app_1Gqj58ynP0mHeX1q",
"channel": "jdpay",
"order_no": "123456789",
"batch_no": null,
"amount": 100,
"amount_settle": 100,
"currency": "cny",
"recipient": "",
"description": "Your Description",
"transaction_no": "5764691151214672907396389219",
"failure_msg": null,
"extra": {
"card_number": "621485022119802",
"user_name": "张三",
"open_bank_code": "0100"
}
}

第四步:接收 Webhooks 通知

转账成功后 Ping++ 会给你配置在 Ping++ 管理平台的 Webhooks 通知地址主动发送转账结果,我们称之为 Webhooks 通知。 Webhooks 通知是以 POST 形式发送的 JSON,放在请求的 body 里,内容是 Event 对象,支付成功的事件类型为 transfer.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 通知地址配置的 transfer.succeeded 对象(以 alipay 渠道为例):

{
"id": "evt_KsW9xvyxur1gbgt7CGOMsgG7",
"created": 1458793624,
"livemode": true,
"type": "transfer.succeeded",
"data": {
"object": {
"id": "tr_0eTi1OGqr9iH0i9CePf1a9C0",
"object": "transfer",
"type": "b2c",
"created": 1467615446,
"time_transferred": 1467615446,
"livemode": true,
"status": "paid",
"app": "app_1Gqj58ynP0mHeX1q",
"channel": "alipay",
"order_no": "123456789",
"amount": 100,
"amount_settle": 100,
"currency": "cny",
"recipient": "13333333333",
"description": "Your Description",
"transaction_no": "2016032321001004920229421275",
"failure_msg": null,
"extra": {
"recipient_name": "张三丰",
"recipient_account_type": "13333333333"
}
}
},
"object": "event",
"pending_webhooks": 0,
"request": "iar_8WTLzTfoofT4TiPaHK5p54Ts"
}

第五步:验证 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\Transfer::retrieve('tr_id');
Transfer.retrieve("tr_id");
var pingpp = require('pingpp')('sk_test_ibbTe5jLGCi5rzfH4OqPW9KC');
pingpp.transfers.retrieve(
"tr_id",
function(err, transfer) {
// YOUR CODE
}
);
pingpp.Transfer.retrieve('tr_id')
Pingpp::Transfer.retrieve("tr_id")
transfer, err := transfer.Get("tr_id")
var tr = Transfer.Retrieve("tr_id");

转账列表查询

\Pingpp\Transfer::all(array('limit' => 3));
Map<String, Object> transferParams = new HashMap<String, Object>();
transferParams.put("limit", 3);
Transfer.all(transferParams);
pingpp.transfers.list({ limit: 3 }, function(err, transfers) {
// Your Code
});
res = pingpp.Transfer.all(limit=3)
Pingpp::Transfer.all(:limit => 3)
params := &pingpp.TransferListParams{}
params.Filters.AddFilter("limit", "", "3")
//设置是不是只需要之前设置的 limit 这一个查询参数
params.Single = true
i := transfer.List(params)
for i.Next() {
c := i.Transfer()
}
var trs=Transfer.List(new Dictionary<string, object> {{"limit", 3}});

注意事项

  1. 针对 微信小程序企业付款、银联电子代付、单笔转账到支付宝账户、通联代付、京东代付 ,必须要进行 SDK 签名验证。
  2. 转账成功及转账失败均为接收到相应的 Webhooks 通知。
  3. 你需要在 Ping++ 的管理平台里填写 Webhooks 通知地址,详见 Webhooks 配置说明,你的服务器需要监听这个地址并且接收 Webhooks 通知,接收到 Webhooks 通知后需给 Ping++ 返回服务器状态 2xx 。此时事件类型是 transfer.succeeded,其字段 data 包含了 object 字段, object 字段的值是一个 Transfer 对象。
  4. 若你的服务器未正确返回 2xx,Ping++ 服务器会在 25 小时内向你的服务器不断重发通知,最多 10 次。Webhooks 首次是即时推送,重试通知时间间隔为 5s、10s、2min、5min、10min、30min、1h、2h、6h、15h,直到你正确回复状态 2xx 或者超过最大重发次数,Ping++ 将不再发送。
  5. 在可接受的时间范围内,如果你服务端没有收到 Webhooks 的通知,你也可以调用 Server-SDK 封装的查询方法,主动向 Ping++ 发起请求来获得订单状态,该查询结果可以作为交易结果。

下一步红包