错误
SDK 报告的每个失败首先回答一个问题:请求到达平台了吗? 答案决定你可以把订单判负,还是必须先查询再决定。代付会动钱,这一步判断错了就会重复出款,或者订单永远挂着。
错误类型
| 含义 | Go | JavaScript | Python | PHP | Java |
|---|---|---|---|---|---|
| 客户端配置无效 | NewClient 返回错误 | ConfigError | ConfigError | ConfigException | DeepaymentException.Config |
| 发送前被拒 | errors.Is(err, ErrInvalidRequest) | RequestError | RequestError | RequestException | DeepaymentException.Request |
| 没有可用响应 | errors.Is(err, ErrTransport) | TransportError | TransportError | TransportException | DeepaymentException.Transport |
| envelope 里的业务错误 | *APIError | APIError | APIError | ApiException | DeepaymentException.Api |
| 非 envelope 响应 | *ResponseError | ResponseError | ResponseError | ResponseException | DeepaymentException.Response |
| 响应超过大小上限 | errors.Is(err, ErrResponseTooLarge) | ResponseTooLargeError | ResponseTooLargeError | ResponseTooLargeException | DeepaymentException.ResponseTooLarge |
| webhook 验签失败 | VerifyWebhook / Parse* 返回错误 | WebhookError | WebhookError | WebhookException | DeepaymentException.Webhook |
Go 以外的类型都派生自一个基类(SDKError、SdkException、DeepaymentException),一个 catch 就能接住 SDK 抛出的全部异常。
分诊
| 错误 | 发生了什么 | 处置 |
|---|---|---|
| 发送前被拒 | 本地校验失败、参数有误,或请求无法编码或签名。什么都没发出去。 | 可以安全判负。修好请求,用同一个 merchantOrderNo 重试。 |
| 没有可用响应 | 连接失败、超时、读取中断。请求可能已到达。 | 结果未知。代付绝不能判负。 用 merchantOrderNo 查询,或用同一个号原样重发。 |
| 业务错误 | 平台返回了格式正确的错误 envelope。 | 按 msg 分支,见下文。 |
| 非 envelope 响应 | 网关或 CDN 返回了 HTML 或纯文本(如 502 页面)。 | 结果未知,先查询再决定。 |
| 响应超过大小上限 | 响应到了但被丢弃。 | 结果未知,订单很可能已创建,先查询再决定。 |
| 其他 | 意料之外。 | 假定请求可能已到达,先查询再决定。 |
业务错误字段
| 字段 | Go | JS | Python | PHP | Java |
|---|---|---|---|---|---|
| HTTP 状态码 | HTTPStatus | httpStatus | http_status | $e->httpStatus | httpStatus |
| 数字错误码 | Code | code | code | $e->getCode() | code |
机器可读 msg | Msg | msg | msg | $e->msg | msg |
| 人类可读文案 | Message | apiMessage | message | $e->apiMessage | apiMessage |
| Trace id | TraceID | traceId | trace_id | $e->traceId | traceId |
| 原始 body | RawBody | rawBody | raw_body | $e->rawBody | rawBody |
每个失败都把 trace id 记进日志,技术支持靠它定位请求。各语言都导出了 msg 常量(Go MsgOrderNotFound、JS MSG.ORDER_NOT_FOUND、Python MSG_ORDER_NOT_FOUND);PHP 和 Java 直接比较字符串。
按 msg 处置
带 HTTP 状态码的完整错误码表见错误码。需要特定处理的情形:
msg | 处置 |
|---|---|
INVALID_FIELD | SDK 不检查的格式在网关校验失败。修正字段;同一个 merchantOrderNo 可以复用。 |
UNAUTHORIZED | 检查服务器时钟、Access Key,以及登记的公钥是否与你的私钥匹配。 |
IDEMPOTENCY_CONFLICT | 号已被占用但平台无法返回原单。查询这个号并持续查询,不要换号。 |
CHANNEL_ERROR | 订单可能已存在。先用 merchantOrderNo 查询;只有查询返回 ORDER_NOT_FOUND 才能复用这个号。 |
CHANNEL_BUSY | 订单创建前就被拒。退避后用同一个号重发;唯一不需要先查询的渠道错误。 |
ORDER_REJECTED | 风控或业务规则。不要用同一请求重试。 |
INSUFFICIENT_BALANCE | 充值后新建代付。 |
RATE_LIMITED | 退避后重发同一请求。 |
SERVICE_UNAVAILABLE、INTERNAL_ERROR | 写操作按结果未知处理:先查询再重发。 |
商户单号是安全网
以上全部建立在一个性质上:用同一个 merchantOrderNo 再次创建绝不会产生第二笔订单。第一次尝试前就生成并持久化这个号,在订单到达终态前的每次查询和重发都复用它。只有真正的新订单才分配新号。