分润模板
接入 Ping++ 分润模板接口,仅需要 Ping++ Server SDK 即可。服务器需要做的就是向 Ping++ 请求创建 Royalty Template 对象,具体步骤如下:
- 设置 API-Key
- SDK 验证签名设置
- 从服务端发起创建分润模板请求,获取 Royalty Template 对象
第一步:设置 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 模式
注意: 一旦上传公钥至 Ping++ 管理平台并启用 Live 模式,则验证签名功能即时生效,Ping++ 会立即验证你的真实线上交易验签请求。如果私钥为空或错误,则会交易失败,所以请确保测试模式正常后再启用 Live 开关。
第三步:从服务端发起创建分润模板请求,获取 Royalty Template 对象
调用 Ping++ Server SDK 发起创建分润模板请求。
try { $royalty_template = \Pingpp\RoyaltyTemplate::create(['app' = > \Pingpp\Pingpp::getAppId(), //应用 ID 'name' = > 'royalty_template_name', // 分润模板名称,允许中英文等常用字符 'rule' = > ['royalty_mode' = > 'rate', //分润模式。分为按订单金额(包含优惠券金额)的比例 rate 和固定金额 fixed 'refund_mode' = > 'no_refund', //退分润模式。分为退款时不退分润 no_refund、按比例退分润 proportional 和一旦退款分润全退 full_refund 'allocation_mode' = > 'receipt_reserved', //分配模式。指当订单确定的层级如果少于模板配置层级时,模板中多余的分润金额是归属于收款方 receipt_reserved 还是服务方 service_reserved。 'data' = > [ ['level' = > 1, //子商户层级值,0 表示平台, 1 表示一级子商户,取值范围 >=0 'value' = > 30 //分润数值。rate 下取值为 0 - 10000,单位为 0.01 %,fixed 下取值为 0 - 1000000,单位为分 ], ['level' = > 2, 'value' = > 20], ['level' = > 3, 'value' = > 10], ], ], 'description' = > 'Your description', ]); echo $royalty_template;} catch (\Pingpp\Error\Base $e) { // 捕获报错信息 if ($e - > getHttpStatus() != NULL) { echo $e - > getHttpStatus().PHP_EOL; echo $e - > getHttpBody().PHP_EOL; } else { echo $e - > getMessage().PHP_EOL; }}
@Testpublic void testRoyaltyTemplateCreate() throws RateLimitException, APIException, ChannelException, InvalidRequestException, APIConnectionException, AuthenticationException { Map < String, Object > params = new HashMap < > (); params.put("app", PingppAccountTestData.getAppID()); // App ID, 必传 params.put("name", "royalty_templates name"); // 模板名称,允许中英文等常用字符, 可选 Map < String, Object > rule = new HashMap < > (); rule.put("royalty_mode", "rate"); // 分润模式。分为按订单金额(包含优惠券金额)的比例 rate 和固定金额 fixed, 必传 rule.put("refund_mode", "no_refund"); // 退分润模式。分为退款时不退分润 no_refund、按比例退分润 proportional 和一旦退款分润全退 full_refund, 必传 // 分配模式。指当订单确定的层级如果少于模板配置层级时,模板中多余的分润金额是归属于收款方 receipt_reserved 还是服务方 service_reserved。 // 必传 rule.put("allocation_mode", "receipt_reserved"); List < Map > data = new ArrayList < > (); // 分润数据列表, 必传 Map < String, Object > data1 = new HashMap < > (); data1.put("level", 0); // 子商户层级值,0 表示平台, 1 表示一级子商户,取值范围 >=0 data1.put("value", 11); // 分润数值。rate 下取值为 0 - 10000,单位为 0.01 %,fixed 下取值为 0 - 1000000,单位为分 Map < String, Object > data2 = new HashMap < > (); data2.put("level", 1); data2.put("value", 12); data.add(data1); data.add(data2); rule.put("data", data); params.put("rule", rule); // 创建 royalty_template 方法 // 参数: params RoyaltyTemplate obj = RoyaltyTemplate.create(params); assertEquals("object should be royalty_template", "royalty_template", obj.getObject()); assertEquals("name", params.get("name"), obj.getName());}
pingpp.royaltyTemplates.create({ 'app': APP_ID, // App ID, 必传 'name': 'royalty_templates name', // 模板名称,允许中英文等常用字符, 可选 'rule': { 'royalty_mode': 'rate', // 分润模式。分为按订单金额(包含优惠券金额)的比例 rate 和固定金额 fixed, 必传 'refund_mode': 'no_refund', // 退分润模式。分为退款时不退分润 no_refund、按比例退分润 proportional 和一旦退款分润全退 full_refund, 必传 'allocation_mode': 'receipt_reserved', // 分配模式。指当订单确定的层级如果少于模板配置层级时,模板中多余的分润金额是归属于收款方 receipt_reserved 还是服务方 service_reserved。必传 'data': [ // 分润数据列表, 必传 { 'level': 0, // 子商户层级值,0 表示平台, 1 表示一级子商户,取值范围 >=0 'value': 11 // 分润数值。rate 下取值为 0 - 10000,单位为 0.01 %,fixed 下取值为 0 - 1000000,单位为分 }, { 'level': 1, 'value': 12 }] }}, function(err, royaltyTemplate) { if (err != null) { console.log(err); } // YOUR CODE});
# api_key获取方式:登录 [Dashboard](https: //dashboard2.pingxx.com)->点击管理平台右上角公司名称->企业面板->开发参数->基础密钥->Test Secret Key 和 Live Secret Keypingpp.api_key = 'sk_test_ibbTe5jLGCi5rzfH4OqPW9KC'app_id = 'app_1Gqj58ynP0mHeX1q'#设置API Keypingpp.api_key = api_key # app_id支持全局配置pingpp.app_id = app_idpingpp.private_key_path = os.path.join(os.path.dirname(os.getcwd()), 'your_rsa_private_key.pem')try :#创建分润结算对象params = { "app": pingpp.app_id, "name": "Your RoyaltyTemplate Name", #模板名称,允许中英文等常用字符"rule": { "royalty_mode": "rate", #分润模式。分为按订单金额(包含优惠券金额)的比例rate和固定金额fixed "refund_mode": "no_refund", #退分润模式。分为退款时不退分润no_refund、按比例退分润proportional和一旦退款分润全退full_refund "allocation_mode": "receipt_reserved", #分配模式。指当订单确定的层级如果少于模板配置层级时,#模板中多余的分润金额是归属于收款方receipt_reserved还是服务方service_reserved。"data": [{ "level": 1, #子商户层级值,0表示平台,1表示一级子商户,取值范围 >= 0 "value": 20 #分润数值。rate下取值为0 - 10000,单位为0.01 % ,fixed下取值为0 - 1000000,单位为分 }, { "level": 2, "value": 10 }] }}royalty_template = pingpp.RoyaltyTemplate.create( * * params)print('create royalty_template', royalty_template)except Exception as e: print(e.http_body)
should "return a royalty_template object when passed correct parameters"doparams = { # App ID required | string: app = > get_app_id, #描述信息optional | string: description = > "TEMPLATE DESCRIPTION", #模版名称optional | string #: name = > "TEMPLATE_NAME", #分润规则required | hash: rule = > { #分润模式required | string; rate: 按订单金额(包含优惠券金额)的比例, fixed: 固定金额: royalty_mode = > "rate", #分配模式required | string;指当订单确定的层级如果少于模板配置层级时,#模板中多余的分润金额是归属于收款方`receipt_reserved`还是服务方`service_reserved`。: allocation_mode = > "receipt_reserved", #退分润模式required | string; no_refund: 退款时不退分润, proportional: 按比例退分润, full_refund: 一旦退款分润全退: refund_mode = > "no_refund", #分润数据列表required | array: data = > [{: level = > 0, : value = > 10 }, {: level = > 1, : value = > 20 }], },} o = Pingpp::RoyaltyTemplate.create(params) assert o.kind_of ? (Pingpp::RoyaltyTemplate)end
func(c * RoyaltyTmplDemo) New()( * pingpp.RoyaltyTmpl, error) { params: = & pingpp.RoyaltyTmplParams { App: c.demoAppID, Name: "royalty_template_name", Rule: pingpp.Rule { Royalty_mode: "rate", Refund_mode: "no_refund", Allocation_mode: "receipt_reserved", Data: [] pingpp.RuleData { pingpp.RuleData { Level: 1, Value: 30, }, pingpp.RuleData { Level: 2, Value: 20, }, pingpp.RuleData { Level: 3, Value: 10, }, }, }, Description: "Your description", } return royaltyTemplate.New(params)}
public static void Example(string appId) { var createParams = new Dictionary < string, object > { { "app", appId }, { "name", "Your Royalty Template Name" }, //模板名称,允许中英文等常用字符 { "rule", new Dictionary < string, object > { { "royalty_mode", "rate" }, //分润模式。分为按订单金额(包含优惠券金额)的比例 rate 和固定金额 fixed { "refund_mode", "full_refund" }, //退分润模式。分为退款时不退分润 no_refund、按比例退分润 proportional 和一旦退款分润全退 full_refund { "allocation_mode", "receipt_reserved" }, //分配模式。指当订单确定的层级如果少于模板配置层级时,模板中多余的分润金额是归属于收款方 receipt_reserved 还是服务方 service_reserved。 { "data", new List < Dictionary < string, object >> { new Dictionary < string, object > { { "level", 1 }, //子商户层级值,0 表示平台, 1 表示一级子商户,取值范围 >=0 { "value", 20 } //分润数值。rate 下取值为 0 - 10000,单位为 0.01 %,fixed 下取值为 0 - 1000000,单位为分 }, new Dictionary < string, object > { { "level", 2 }, { "value", 10 } } } } } } };}
Ping++ 收到创建分润模板请求后返回给你的服务器一个 Royalty Template 对象,下面是 Royalty Template 的一个示例:
{ "id": "450170814144600001", "object": "royalty_template", "livemode": false, "app": "app_1Gqj58ynP0mHeX1q", "name": "royalty_templates name", "created": 1502693202, "description": null, "rule": { "royalty_mode": "rate", "refund_mode": "no_refund", "allocation_mode": "receipt_reserved", "data": [{ "level": 0, "value": 11 }, { "level": 1, "value": 12 }] }}
分润模板查询
Ping++ 提供分润模板查询接口可以查询单个分润模板以及分润模板列表。
单个分润模板查询
try { $royalty_tpl_info = \Pingpp\RoyaltyTemplate::retrieve('451170807182300001'); echo $royalty_tpl_info;} catch (\Pingpp\Error\Base $e) { // 捕获报错信息 if ($e - > getHttpStatus() != NULL) { echo $e - > getHttpStatus().PHP_EOL; echo $e - > getHttpBody().PHP_EOL; } else { echo $e - > getMessage().PHP_EOL; }}
@Test public void testRoyaltyTemplateRetrieve() throws RateLimitException, APIException, ChannelException, InvalidRequestException, APIConnectionException, AuthenticationException { String id = "450170822152200001"; // 查询单个 royalty_template 方法 // 参数: royalty_template id RoyaltyTemplate obj = RoyaltyTemplate.retrieve(id); assertEquals("object should be royalty_template", "royalty_template", obj.getObject()); assertEquals("id", id, obj.getId());}
pingpp.royaltyTemplates.retrieve('450170830143400001', // RoyaltyTemplates ID function(err, royaltyTemplate) { if (err != null) { console.log(err); } // YOUR CODE});
print('retrieve royalty_template', pingpp.RoyaltyTemplate.retrieve(royalty_template.id))
should "return an existed royalty_template object when passed a correct id"doo = Pingpp::RoyaltyTemplate.retrieve(existed_royalty_template_id) assert o.kind_of ? (Pingpp::RoyaltyTemplate)end
func(c * RoyaltyTmplDemo) Get()( * pingpp.RoyaltyTmpl, error) { return royaltyTemplate.Get(c.demoroyaltyTmplId)}
Console.WriteLine("**** 查询分润模板 ****");Console.WriteLine(RoyaltyTemplate.Retrieve("451170814105500001"));
分润模板列表查询
try { $params = ['page' = > 1, 'per_page' = > 10, ]; $royalty_tpl_info = \Pingpp\RoyaltyTemplate::all($params); echo $royalty_tpl_info;} catch (\Pingpp\Error\Base $e) { // 捕获报错信息 if ($e - > getHttpStatus() != NULL) { echo $e - > getHttpStatus().PHP_EOL; echo $e - > getHttpBody().PHP_EOL; } else { echo $e - > getMessage().PHP_EOL; }}
@Test public void testRoyaltyTemplateList() throws RateLimitException, APIException, ChannelException, InvalidRequestException, APIConnectionException, AuthenticationException { Map < String, Object > params = new HashMap < > (); params.put("per_page", 3); params.put("page", 1); // 查询 royalty_template list 列表方法 // 参数: params RoyaltyTemplateCollection objs = RoyaltyTemplate.list(params); assertEquals("object should be list", "list", objs.getObject());}
pingpp.royaltyTemplates.list({ page: 1, per_page: 3}, function(err, data) { if (err != null) { console.log(err); } // YOUR CODE});
print("retrieve royalty_template list", pingpp.RoyaltyTemplate.all())
should "return a list object of royalty_template"doo = Pingpp::RoyaltyTemplate.list({: per_page = > 3, : page = > 1}) assert o.kind_of ? (Pingpp::ListObject)assert o.data[0].kind_of ? (Pingpp::RoyaltyTemplate)end
func(c * RoyaltyTmplDemo) List()( * pingpp.RoyaltyTmplList, error) { params: = & pingpp.PagingParams {} params.Filters.AddFilter("page", "", "1") params.Filters.AddFilter("per_page", "", "10") return royaltyTemplate.List(params)}
Console.WriteLine("**** 查询分润模板列表 ****");Console.WriteLine(RoyaltyTemplate.List(new Dictionary < string, object > { { "page", 1 }, { "per_page", 10 }}));
注意事项
- 分润模板和子商户的创建顺序没有限制,但需要保证在订单创建前创建完成。
- 分润模板支持修改和删除。但如果在修改或删除之前将模板传入订单,这些订单不受影响。
- 分润模板只能由平台创建、管理,子商户不能查看或管理分润模板。
- 平台拥有多个分润规则业务时,可以创建多个模板,在订单创建时指定对应规则的分润模板 ID 即可。
- 在订单创建时使用了分润模板后,在退款时分润模板会自动生效,此时如果填写了退分润用户列表,则分润模板不生效,以传入的为准。
下一步分润结算