退款申请回调扩展点

更新时间 2024-07-24 02:58:49
收藏
我的收藏

使用限制​

    1.接入前,请先查看接入前准备是否完成​
    2.接入前,请先阅读接入指引-交易退款

接口说明​

    用户发起/抖音客服发起退款后,交易系统都会请求开发者的“退款申请回调”接口 ​
    退款单创建是在退款申请回调之前,退款申请回调请求开发者服务失败,不会导致退款失败,会导致退款卡单。系统在72小时内会重试, 重试频率是10分钟/次。若72小时后开发者未成功响应,系统会自动通过,并生成默认外部退款单号和退款详情页schema。​
    开发者响应的外部退款单号请务必确保在同一小程序内不会重复,否则也会被认为请求失败。 ​
    开发者返回的 err_no 不为 0 也会认为请求失败,会进行重试,请确保请求成功,有拒绝退款的场景请在退款审核阶段拒绝退款。 ​
    退款申请回调务必做好幂等处理,相同的交易系统退款单号重复请求应当被视为相同的一次退款。 ​
    下图分别展示了用户退和客服退的流程:​
用户退
客服退

基本信息 ​

基本信息 ​
HTTP URL
在解决方案配置-扩展点中指定的回调地址,配置方式参考解决方案配置文档
HTTP Method
POST ​
权限要求
无 ​

请求头 ​

请求参数 ​

名称 ​
类型 ​
是否必填 ​
描述 ​
示例 ​
msg ​
string​
是 ​
退款单相关信息的 json 字符串 ​
ot1231231 ​
type ​
string​
是 ​
枚举值: pre_create_refund 退款申请回调 ​
pre_create_refund ​
version ​
string​
是 ​
固定值:"3.0"。 回调版本,用于开发者识别回调参数的变更 ​
3.0​
msg 说明 (msg 是 json 格式字符串)
名称 ​
类型 ​
是否必填 ​
描述 ​
示例 ​
app_id ​
string​
是 ​
小程序的 appid ​
ttqweqw12312 ​
open_id ​
string​
是 ​
用户 openid ​
123123​
refund_id ​
string​
是 ​
抖音开平侧退款单号 ​
ot1231313 ​
order_id ​
string​
是 ​
抖音开平侧订单号 ​
ot1231312 ​
out_order_no ​
string​
是 ​
开发者侧订单号 ​
213123​
refund_total_amount ​
int64​
是 ​
退款总金额,单位分 ​
100​
need_refund_audit​
int8​
是​
是否需要退款审核:​
1:需要退款审核 ​
2:不需要退款审核​
不需要退款审核,则无需再调用退款审核结果同步接口
1​
refund_audit_deadline ​
int64​
否​
退款审核的最后期限,超过该期限无需商家审核,自动退款,13 位时间戳,单位毫秒 通常是3天(从退款发起时间开始算) ​
151231321231​
create_refund_time ​
int64​
是 ​
退款创建时间,13 位时间戳,单位毫秒 ​
151231321230​
refund_source ​
int8​
是 ​
退款来源: 1:用户发起退款 4:抖音客服退款 ​
1​
cp_extra ​
string​
否 ​
cp 自定义字段,不支持二进制,长度 <= 512byte ​
cp_extra ​
refund_reason ​
Array​
否 ​
退款原因,退款原因有多个 ​
["不喜欢"] ​
refund_description ​
string​
否 ​
退款补充说明 ​
想退款 ​
refund_item_detail ​
object​
否 ​
退款商品单信息​
refund_item_detail 说明
名称 ​
类型 ​
是否必填 ​
描述 ​
示例值 ​
item_order_quantity ​
int64​
是 ​
用户需要退款多少个商品单 ​
1​
item_order_detail ​
Array<object>​
是 ​
本次退款的商品单列表 ​
item_order_detail 说明
名称 ​
类型 ​
是否必填 ​
描述 ​
示例值 ​
item_order_id ​
string​
是 ​
抖音开平侧商品单号,商品单号在预下单接口同步给了开发者 ​
motb123134 ​
refund_amount ​
int​
是 ​
该商品单退款金额 ​
100​

请求示例 ​

Plain Text
复制
curl --location --request POST 'https://xxxxxxx.net/api/v2/create_refund?timestamp=1345678901234&nonce=iuy987q4htafreqw' \
--header 'Content-Type: application/json' \
--header 'Byte-Authorization: SHA256-RSA2048 appid="ttxxx",nonce_str="DC10180A100073E70A48F195DA2AF2E6",timestamp="1623934869",key_version="1",signature="nwd1L3wCX+01/TVTkILeovF1DtYeghC1VHjrcjTHVkh7+gRaONEQkC2Y72Mw8JdSnIyeAtyp/pDHzyKGywjVqv5+JOBEhQG1/pvwNHN49wD26qg3AJL4hXw0fMJSRiTQEV1MszwDLuaabvo/qM9OXL9KyYiEPwVJqYtzmho4cHXT6mYgzNOW1xt5d7RDf4QO74JI3i4dtk9Uj8svJTrrBabML6AUcqcx2OP/7xukdaUgPdPf+IqmMG6GC4n52LUDogcL5n/osLdfHg9l6kW5gDcDjBfNDaggz07QMPHGdVao7pnQ2ub7VqcFIuY6Q3cBL7ndQdDGKrv+WBy5Q90QjQ=="'
--data-raw='{
"version": "3.0",
"msg": "{\"app_id\":\"ttqweqw12312\",\"open_id\":\"123123\",\"refund_id\":\"ot123133\",\"order_id\":\"motb1231312\",\"out_order_no\":\"213123\",\"refund_total_amount\":100,\"need_refund_audit\":1,\"refund_audit_deadline\":151231321231,\"create_refund_time\":151231321230,\"refund_source\":1,\"refund_reason\":[\"不喜欢\"],\"refund_description\":\"想退款\",\"cp_extra\":\"cp_extra\",\"refund_item_detail\":{\"item_order_quantity\":1,\"item_order_detail\":[{\"item_order_id\":\"motb123134\",\"refund_amount\":100}]}}",
"type": "pre_create_refund"
}'
交易系统退款单 msg 示例 ​
Plain Text
复制
{
"app_id": "ttqweqw12312",
"open_id": "123123",
"refund_id": "motb76123133",
"order_id": "motb7823133",
"out_order_no": "213123",
"refund_total_amount": 100,
"need_refund_audit": 1,
"refund_audit_deadline": 151231321231,
"create_refund_time": 151231321230,
"refund_source": 1,
"refund_reason": ["不喜欢"],
"refund_description": "想退款",
"cp_extra": "cp_extra",
"refund_item_detail": {
"item_order_quantity": 1,
"item_order_detail": [
{
"item_order_id": "motb71123134",
"refund_amount": 100
}
]
}
}

响应参数 ​

名称 ​
类型 ​
是否必填 ​
描述 ​
示例 ​
err_no ​
int64​
是 ​
状态码 0 表示业务处理成功,具体错误码参见后文错误码章节 ​
0​
err_tips ​
string​
是 ​
错误提示信息 ​
success ​
data ​
object​
是 ​
返回数据信息 ​
data 信息
名称 ​
类型 ​
是否必填 ​
描述 ​
示例 ​
out_refund_no ​
string​
是 ​
开发者侧退款单号,长度 <= 64byte ​
123213​
order_entry_schema ​
object​
是 ​
退款单详情页跳转地址 ​
notify_url ​
string​
否 ​
退款结果通知地址,必须是 https 类型。若不填,默认使用抖音开放平台-小程序应用详情-能力-支付页面设置的回调地址,长度 <= 512byte ​
https://xxx ​
order_entry_schema 说明
名称 ​
类型 ​
是否必填 ​
描述 ​
示例值 ​
path ​
string​
是 ​
订单详情页路径,没有前导的/,该字段不能为空,长度 <= 512byte ​
pages/xxxxxxx ​
params ​
string​
否 ​
路径参数,自定义的 json 结构,序列化成字符串存入该字段,平台不限制,但是写入的内容需要能够保证生成访问订单详情的 schema 能正确跳转到小程序内部的订单详情页,长度 <= 512byte ​
{\"id\":\"xxxxxx\"} ​

响应示例 ​

正常示例 ​
Plain Text
复制
{
"err_no": 0,
"err_tips": "123213",
"data": {
"out_refund_no": "89876867867087",
"order_entry_schema": {
"path": "page/refundDetail/xxx",
"params": "{\"id\": 1}"
},
"notify_url": "https://xxx"
}
}
异常示例 ​
Plain Text
复制
{
"err_no": 1,
"err_tips": "error"
}

常见问题​

1.哪些情况下交易系统会回调开发者

A: 用户/抖音客服发起的退款都会回调开发者。即,所有非开发者通过服务端 openAPI 发起的退款,在成功创建退款单后系统都会回调开发者。​

2.开发者已经响应了退款申请回调,为什么系统还一直在重试

A: 请按以下步骤排查,​
a. 请检查接口响应是否符合要求,比如out_refund_no是否唯一,order_entry_schema.params字段是否合法的json字符串​
b. 可能是超时/网络原因导致,请保证接口幂等返回​

3.若开发者未响应退款申请回调,系统会重试多久

A: 系统会一直重试,直到成功。未回调成功的退款单会处于卡单状态,会触发系统告警。若对退款有疑问,请咨询行业运营。​

4.为什么开发者未发起退款,但是收到了退款申请回调

A: 除了开发者发起退款,还存在用户通过抖音平台提供的入口发起退款、抖音客服发起等场景,请通过查询退款接口查询订单的退款记录,并检查 refund_source 字段,可以获得具体的退款来源。​

退款申请回调接口排查指引​

若遇到退款单一直处于退款中、审核同步失败的场景,大多数情况是由于没有正确响应退款申请回调导致的。这里提供通用的排查方案,请按步骤执行。​
退款申请回调接口超时时间为 2s,请确保你的服务响应在 2s 内​

1.获取小程序配置的退款申请回调地址​

退款申请回调地址是开发者在解决方案配置-扩展点中指定的回调地址,配置方式参考解决方案配置文档
找到你配置的 https url 地址​

2.检查退款回调地址能否调通​

执行下面的 curl 命令,如果 http 响应不是 200,说明回调地址不通,请检查你的服务。​
text
复制
curl -X POST '你的退款申请回调地址' -H 'Content-Type:application/json' --data '{
"version": "3.0",
"msg": "",
"type": "pre_create_refund"
}'
如果确认你的回调地址是通的,再进行下一步响应参数检查​

3.校验响应参数​

a. copy 下面的 python 脚本,保存为check_your_resp.py
b. copy 你的退款申请回调响应​
c. 参考下面这个示例,把响应替换成你自己的,然后执行下面的命令​
Python
复制
python check_your_resp.py '{
"err_no":0,
"err_tips":"success",
"data":{
"out_refund_no":"id12348473",
"order_entry_schema":{
"path":"page/refundDetail/xxx",
"params":"{\"id\":1}"
},
"notify_url":"https://www.abc.com"
}
}'
    如果执行结果是“验证成功!”,说明响应参数是OK的​
    如果执行结果有误,请按照提示调整你的参数​
python 校验脚本​
Python
复制
# -*- coding: utf-8 -*-
from jsonschema import validate, draft7_format_checker
from jsonschema.exceptions import SchemaError, ValidationError
import json
import sys
# schema 不要修改!!
schema = {
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "test demo",
"description": "validate result information",
"type": "object",
"properties": {
"err_no": {
"description": "error code",
"type": "integer"
},
"err_tips": {
"description": "error msg ",
"type": "string"
},
"data": {
"description": "response date",
"type": "object",
"properties": {
"out_refund_no": {
"type": "string","minLength": 1,"maxLength": 64
},
"notify_url": {
"type": "string",
"pattern":"^$|^https://[a-zA-Z0-9\\.\\?/%-_]*$","minLength": 0,"maxLength": 512
},
"order_entry_schema": {
"type": "object",
"properties": {
"path": {
"type": "string","minLength": 1,"maxLength": 512
},
"params": {
"type": "string","minLength": 0,"maxLength": 512
}
}
}
},
"required": ["out_refund_no", "order_entry_schema"]
}
},
"required": [
"err_no", "err_tips", "data"
]
}
# 校验方法, 不要修改!!
def check_your_resp(resp):
try:
validate(instance=resp, schema=schema, format_checker=draft7_format_checker)
except SchemaError as e:
print("验证模式schema出错:\n出错位置:{}\n提示信息:{}".format(" --> ".join([i for i in e.path]), e.message))
except ValidationError as e:
print("json数据不符合schema规定:\n出错字段:{}\n提示信息:{}".format(" --> ".join([i for i in e.path]), e.message))
else:
params = resp.get("data",{}).get("order_entry_schema",{}).get("params","")
if len(params) > 0:
try:
obj = json.loads(params)
if type(obj) != dict or len(obj) < 1:
print("data-->order_entry_schema-->params字段必须是序列化后json字符串")
return
except:
print("data-->order_entry_schema-->params字段必须是序列化后json字符串")
return
print("验证成功!")
if __name__ == '__main__':
if len(sys.argv)!=2:
print("执行错误, 正确的命令为: python check_your_resp.py your_resp_json")
exit()
try:
resp = json.loads(sys.argv[1])
if type(resp) != dict:
print("执行错误, 输入参数必须是合法的json字符串")
exit()
check_your_resp(resp)
except:
print("执行错误, 输入参数必须是合法的json字符串")