tt.createSignOrder
收藏
我的收藏周期代扣签约下单接口
调用该 API 后,会生成相关签约单,用于后续唤起签约页面(调用
tt.sign
) 。使用限制
最低支持版本上注明行业 SDK ,表示仅在行业 SDK 上才支持,需要在代码中配置行业 SDK 的权限:行业 SDK 的权限配置。当完成行业 SDK 的权限配置后,可通过 tt.canIUse('createSignOrder') 判断该 API 是否可用。
语法
tt.createSignOrder(options)
参数说明
options 为 object 类型,属性如下:
属性名 | 类型 | 默认值 | 必填 | 说明 |
businessType | number | - | 是 | 预授权模式,枚举值 2 - 周期代扣 |
data | string | - | 是 | 由开发者服务端返回,具体格式参考 data |
byteAuthorization | string | - | 是 | 签名信息,由开发者服务端返回,生成方式见下文 |
success | function | - | 否 | 成功回调 |
fail | function | - | 否 | 失败回调 |
complete | function | - | 否 | 完成回调 |
data 说明
属性名 | 类型 | 必填 | 说明 |
outAuthOrderNo | string | 是 | 开发者侧签约单的单号,长度<=64byte |
serviceId | string | 是 | 签约模板ID |
openId | string | 是 | 用户 openId |
expireSeconds | int64 | 否 | 签约或签约支付超时时间,单位[秒],不传默认5分钟,最少30秒,不能超过48小时。 建议开发者不要将超时时间设置太短 |
notifyUrl | string | 否 | 签约结果回调地址,https开头,长度<=512byte |
firstDeductionDate | string | 否 | 首次扣款日期,格式YYYY-MM-DD,纯签约场 景需要传入,用于c端展示 |
authPayOrder | object | 否 | 扣款信息,如果传入该字段则会走签约支付流程,否则走纯签约流程 |
onBehalfUid | string | 否 | 代签约用户uid,该uid必须由ASCII字母、数字、下划线组成,长度<=64个字符,通常该字段应填入开发者自己系统的uid, |
uniqueId | string | 否 | 开发者指定签约唯一键,用户防止重复签约。
|
signWay | int32 | 否 | 签约渠道: 2 - 支付宝。
3 - 抖音支付 |
authPayOrder 说明
属性名 | 类型 | 必填 | 说明 |
outPayOrderNo | string | 是 | 开发者侧代扣单的单号,长度<=64byte |
merchantUid | string | 是 | 开发者自定义收款商户号,限定在在小程序绑定的商户号内 |
initialAmount | int64 | 否 | 首期代扣金额,单位[分],不传则使用模板上的扣款金额,签约模板支持前N(N<=6)期优惠,该字段优先级高于模板的配置的第一期优惠价格,举例:如果当前参数传入扣款金额为10元,而实际模板中配置的第一期 优惠价格为20元,那么第一期的实际扣款金额是10元 |
notifyUrl | string | 否 | 支付结果回调地址,https开头,长度<=512byte |
回调成功
object 类型,属性如下:
属性名 | 类型 | 说明 |
errMsg | string | "createSignOrder:ok" |
authOrderId | string | 平台侧签约单的单号,长度<=64 byte |
payOrderId | string | 平台侧代扣单的单号,长度<=64 byte 只有签约并支付场景才会返回 |
回调失败
object 类型,属性如下:
属性名 | 类型 | 说明 |
errNo | string | 错误码 |
errMsg | string | "createSignOrder:fail " + 详细错误信息 |
错误说明
errNo | errMsg | 说明 |
20000 | createSignOrder:fail expect params.businessType to be one of 2, but got undefined | businessType 未传。参数异常,其他字段校验也会有类似错误信息 |
10000 | 参数错误 | 对照错误提示和接口字段定义,检查对应的参数 |
12001 | 操作过于频繁,请稍后再试 | 请等待2-3秒后,再重试 |
12002 | 您涉及违规操作,暂时无法使用该功能 | 风控策略拦截,请联系客服或已对接的运营 |
13000 | 系统错误 | 请重试,若多次重试仍然报错,请联系oncall |
26003 | 小程序违规,支付能力被封禁 | 支付能力被封禁了,请联系运营处理 |
26006 | 商户号与小程序的支付产品不一致 | 商户号未在云直通进件,本期仅支持云直通商户收款 |
26009 | 该小程序未开通支付能力,请先开通支付能力 | 商户号未进件成功,请检查商户号的进件状态 |
32007 | 当前场景暂不支持签约 | 以下场景暂不支持签约:
|
32007 | 签约模板错误 | 请根据错误提示修改签约模板 |
32008 | 已创建一笔有效签约单,请勿重复创建 | 单个用户只能创建一笔进行中的签约单,不允许重复创建。 注意,如果出现这个错误,说明已经为用户创建了一笔进行中的签约单,开发者可以有两种处理方法:
|
byteAuthorization 说明
重要提醒
- 1.加验签操作均需由开发者服务端处理
- 2.首次接入时,开发者需生成应用公私钥,并上传应用公钥至平台,具体操作详见应用密钥
第一步,构造待签名串:
待签名串一共有五 行,每一行必须以
\n
(换行符,ASCII 编码值为 0x0A)结束。POST\n/createSignOrder\n请求时间戳\n请求随机串\ndata\n
第二步,计算签名值:
使用「应用私钥」对待签名串进行
SHA256-RSA2048
签名,并对签名结果进行Base64
编码得到签名值。第三步,生成 byteAuthorization :
以下为具体格式:
SHA256-RSA2048 应用appid,请求随机串nonce_str,请求时间戳timestamp,公钥版本key_version,签名值signature
示例:
SHA256-RSA2048 appid=tt8629f0941xxxxxxxx,nonce_str=7CC7D26A52F05BA5CFD,key_version=1,timestamp=1698916641,signature=jz1+pplJX2IRcS24lsdsFFOF7+Wky3KiQhOMUz58u7n5VEDNr/axPLzm8+i232Aw4vxjoxavI77n8iUSaGcjHOXS1e4pkdJEHE1qQkQ1bNGsT1X2yXOMB5t8gceGiBJimwQkTSeq+KK/OzYTlCRaNuE784Mx7wtUdXCnBDKZDMxWBxu6R7L2eZ8mC5v67lV+oJcu7tRD10PePRXH1z+9zaX1CSwEg3K0IkLFxHqgrpsER7OeVBE8w9glsxjvuc2Gg==
示例代码:
JAVA
GO
PHP
import java.security.PrivateKey; import java.security.Signature; import java.nio.charset.StandardCharsets; import java.util.Base64; import java.security.KeyFactory; import java.security.spec.PKCS8EncodedKeySpec; import java.util.UUID; public class DYSign { public static void main(String []args) { // 请求时间戳 long timestamp = System.currentTimeMillis()/1000L; // 开发者填入自己的小程序app_id String appId = "testAppId"; // 随机字符串 String nonceStr = UUID.randomUUID().toString(); // 应用公钥版本,每次重新上传公钥后需要更新,可通过「开发管理-开发设置-密钥设置」处获取 String keyVersion = "1"; // 应用私钥,用于加签 重要:1.测试时请修改为开发者自行生成的私钥;2.请勿将示例密钥用于生产环境;3.建议开发者不要将私钥文本写在代码中 String privateKeyStr = "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCZSHNcFfthd/bV\nYexEJWOBVEjjDcXjfr1fYevuraNFfMmLPKV836BbvCiUSWHzJYEpkJ934e/j28NB\nEcEbPDLiGlLTd6AVwR22TkUwpLr41oQprz0HKFwhVPZ0HQCGIv0pVMA53TFSitIq\niqbNLmgm5yzSNqNy1t/0X/RfqEtA6Eoxw9u/Sx57i+pBFuLlZYanlm57+b7t1khg\n9JHvF0ulo7DScyJ4qgrD7oQf0RIQB0rqCFIeYuYO1cfvnxb9x4DPodEyVoAM4i9Y\ndFop9ZHt73W/icuLku/P8/G1+arzB5b7S1S3ky5/KdS8AEA9Ww5czZcdf9Jgm2S6\nRymjFGjzAgMBAAECggEBAIryGNgdePyWcSJmHHR9a+CdFWD0aDBa/7CJpAN8VKc1\ngcB8Xgp+7+6X9jTM/EQa+CVEWrmiDgF/gVPnkyNsAzff4rqcEnoFzzglZSS9/lp4\nod7jYa+uTy1LxgflDkeJSfEASStqrT4EZpR3kNInQfQZ1BBNxQXhb6smm+9mL6kK\nQJjAqBgEqtUAmNv0GnH89ZPPgZuIZeeL4cb4BhMEoa5MBnI+HDf07cN1nECQXRJl\nHU/iyhAPfP7RpO8O9KGDEDE36qebu0Cu4yUjWANXiqECFv93sQzONotkl3VPealv\nXM+jzGT7YdgHo/t3QKE8flMBo/XUzGTqi8j5AOXiaBkCgYEAylKVtjQMYgg4qMwd\n9Je+KZ9qL6QVHCsB2NPUt8N99oj70efsG4aGaEAadr8meNhIJ5lpoK+FXqSBIbbD\nS/xeOVI3XoMx/EdKLw/ZNi87G/EHYK9z7Fr3W7q8DFXe2hZ/ojFXC/aaBanjVVBK\n/6RfPzXfnx+vGX/t1FhcLC+yQD8CgYEAwfMtrXfH+3dW77dxXT/CTFJVs/o1K2qb\nepnQ6A33KMHPLBtPZZ6z5rzIO7OMSNItOTXTEoRXHmOKc5FtXGtbCvGSRByb6FgD\nWG3p2Bp2sZLuJBzXmLbSnEbHTNHM6uTgxNgWAh8pYpjPY8xF7BqYz2rGT47OPBmc\ntRzDjnzjak0CgYAqkM1mk/S2+zvQZ4E14GblouBYPZEjZ/jvgUGTl9F8eL1iIAUQ\nlXDZpgLrULPrYLVtf101rTfF/Z4dVbIo3mOEc8OqYre1d9onpJHyUGWDL2Z59O/S\nniDEb7j4b2h/QZSArxi9L5if8GofnNDqj85qIg92Dthr6PpEXoKl2TMLSQKBgFzQ\nBHHYukiqSV4ZyRQ4qMBhPkYMXFlUgObgqMoDtN06MewHfa1BjxHCEYgQWfeXLLEO\nAt3/mrkeJWk8lLr/XOgVxkr17d34EFHG93rE3zwG9hMuAjZAdvT2IfWvCIL32GAa\nkB2fz+ww+D3nySY9bBcGH7R+wE6eaxF4nFSZizKZAoGBAJzuaWCnVK0djfgvUsjm\nGUtyDvgyREcpAsXvES1pB2NLVeEUxm0uRtj6k4DhCv3rJfUwfMr0+sa9NUnXuaSR\nVqLYvAD8bNPKXwn7ymzQ7WioCqmZuUhLnQRppkjhfQGKLH0MnMw9Xh9FwJ9kzGNE\nUnTEhaaHsoaHMlLlRET32gyG".replace("\n",""); // 生成好的data String data = "{\"skuList\":[{\"skuId\":\"1\",\"price\":9999,\"quantity\":1,\"title\":\"标题\",\"imageList\":[\"https://dummyimage.com/234x60\"],\"type\":401,\"tagGroupId\":\"idxxx\"}],\"outOrderNo\":\"1213\",\"totalAmount\":9999,\"limitPayWayList\":[],\"payExpireSeconds\":3000,\"orderEntrySchema\":{\"path\":\"page/index/index\",\"params\":\"{\\\"poi\\\":\\\"6601248937917548558\\\",\\\"aweme_useTemplate\\\":1}\"}}"; String byteAuthorization = getByteAuthorization(privateKeyStr, data, appId, nonceStr, timestamp, keyVersion); System.out.println(byteAuthorization); } public static String getByteAuthorization(String privateKeyStr, String data, String appId, String nonceStr, long timestamp, String keyVersion) { String byteAuthorization = ""; try { // 生成签名 String signature = getSignature(privateKeyStr, "POST", "/createSignOrder", timestamp, nonceStr, data); // 构造byteAuthorization StringBuilder sb = new StringBuilder(); sb.append("SHA256-RSA2048 "). append("appid=").append(appId).append(","). append("nonce_str=").append(nonceStr).append(","). append("timestamp=").append(timestamp).append(","). append("key_version=").append(keyVersion).append(","). append("signature=").append(signature); byteAuthorization = sb.toString(); } catch (Exception ex) { ex.printStackTrace(); return ""; } return byteAuthorization; } public static String getSignature(String privateKeyStr, String method, String uri, long timestamp, String nonce, String data) throws Exception { String rawStr = method + "\n" + uri + "\n" + timestamp + "\n" + nonce + "\n" + data + "\n"; Signature sign = Signature.getInstance("SHA256withRSA"); sign.initSign(string2PrivateKey(privateKeyStr)); sign.update(rawStr.getBytes(StandardCharsets.UTF_8)); return Base64.getEncoder().encodeToString(sign.sign()); } public static PrivateKey string2PrivateKey(String privateKeyStr) { PrivateKey prvKey = null; try { byte[] privateBytes = Base64.getDecoder().decode(privateKeyStr); PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateBytes); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); prvKey = keyFactory.generatePrivate(keySpec); } catch (Exception ex) { ex.printStackTrace(); } return prvKey; } }
密钥生成
适用于JAVA(PKCS8格式)
非JAVA适用(PKCS1格式)
openssl genrsa -out private_key.pem 2048 rsa -in private_key.pem -pubout -out public_key.pem pkcs8 -topk8 -inform PEM -in private_key.pem -outform PEM -nocrypt -out rsa_private_key.pem rsa -in rsa_private_key.pem -pubout -out rsa_public_key.pem exit // 注:生成出来的私钥文件为rsa_private_key.pem, 公钥文件为rsa_public_key.pem // PKCS8格式,私钥以-----BEGIN PRIVATE KEY-----开头,-----END PRIVATE KEY-----结尾
应用公钥上传方式
代码示例
tt.createSignOrder({ businessType: 2, data: 'test data', byteAuthorization: 'test byteAuthorization', success: (res) => { tt.sign({ businessType: 2, orderId: res.authOrderId, success: (res) => { this.setData({ result: '签约成功回调: ' + JSON.stringify(res), }) } }) }, fail: (res) => { this.setData({ result: '失败回调: ' + JSON.stringify(res), }) } })
Bug&Tip
无