• JS API 列表
  • 基础
  • TTML
  • 网络
  • 媒体
  • 地图
  • 文件
  • 数据缓存
  • 地理位置
  • 设备
  • 画布
  • 界面
  • 页面导航
  • 开放接口
  • 行业开放
  • 交易系统
  • 通用交易系统
  • 交易工具
  • tt.sign
  • tt.createSignOrder
  • diamond-balance钻石充值与余额组件
  • 第三方平台
  • 其它
  • 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
    开发者指定签约唯一键,用户防止重复签约。
      如果传入该字段,平台使用小程序 id + 模板 id + uniqueId 进行重复签约校验
      如果为空,使用小程序 id + 模板 id + 抖音uid + 代签约用户 id 进行重复签约校验
    signWay
    int32
    签约渠道:
    2 - 支付宝。
    备注:
      1.支付宝仅支持签约并支付模式,使用支付宝签约时 authPayOrder 必填
      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
    当前场景暂不支持签约
    以下场景暂不支持签约:
      1.用户发起签约设备为 iOS 平台(IPhone,IPad)
      2.当前有处理中的代扣订单,为防止重复支付,请等待代扣中订单处理完成
      3.周期类型为自然日以外的类型
    32007
    签约模板错误
    请根据错误提示修改签约模板
    32008
    已创建一笔有效签约单,请勿重复创建
    单个用户只能创建一笔进行中的签约单,不允许重复创建。
    注意,如果出现这个错误,说明已经为用户创建了一笔进行中的签约单,开发者可以有两种处理方法:
      1.实现接口幂等。对于同一个用户,在签约单有效期内,开发者调用本接口时传入相同的入参,本接口会幂等返回(平台侧签约单的单号)。
      2.若开发者已有平台侧签约单的单号,可直接进行下一步tt.sign。

    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