Skip to content

支付分从业机构-商户预下单通知(JSON)

微信支付分通过商户预下单通知告知商户对待支付的支付分服务订单生成收银台订单,以便用户通过收银台完成对服务订单的主动支付。

注意:

  • 同样的通知可能会多次发送给商户系统。商户系统必须能够正确处理重复的通知。 推荐的做法是,当商户系统收到通知进行处理时,先检查对应业务数据的状态,并判断该通知是否已经处理。如果未处理,则再进行处理;如果已处理,则直接返回结果成功。在对业务数据进行状态检查和处理之前,要采用数据锁进行并发控制,以避免函数重入造成的数据混乱。
  • 对后台通知交互时,如果微信收到商户的应答不符合规范或超时,微信认为通知失败,微信会通过一定的策略定期重新发起通知,尽可能提高通知的成功率,但微信不保证通知最终能成功。(通知频率为15s/15s/30s/3m/10m/20m/30m/30m/30m/60m/3h/3h/3h/6h/6h - 总计 24h4m)
请求参数类型描述
headersobject通知的头参数
Content-Typestringapplication/json
Request-IDstring通知的唯一标识
Wechatpay-Noncestring数据签名使用的随机串
Wechatpay-Serialstring平台证书序列号/平台公钥ID
Wechatpay-Signaturestring签名串
Wechatpay-Signature-Typestring签名算法
WECHATPAY2-SHA256-RSA2048 枚举值
Wechatpay-Timestampstring时间戳
bodyobject通知的JSON数据结构
idstring通知的唯一ID
create_timestring通知创建的时间
event_typestring通知的类型
PAYSCORE.MCH_PREPAY 枚举值
resource_typestring通知的资源数据类型
summarystring回调摘要
resourceobject通知资源数据
algorithmstring对数据进行加密的加密算法
AEAD_AES_256_GCM 枚举值
associated_datastring数据加密的附加数据
noncestring加密使用的随机串
ciphertextstring加密后的密文数据
original_typestring原始回调类型
service_idstring调用创单接口时提交的服务ID
appidstring调用创单接口时提交的应用ID
mchidstring调用创单接口时提交的商户号
sub_appidstring调用创单接口时传入的子商户应用ID
sub_mchidstring调用创单接口时传入的子商户商户号
channel_idstring调用创单接口时传入的渠道商商户号
out_order_nostring调用创单接口时提交的商户服务订单号
openidstring微信用户在商户对应AppID下的唯一标识。(传了sub_appid的情况下则只返回sub_openid)
sub_openidstring微信用户在商户对应sub_appid下的唯一标识。(传了sub_appid的情况下则只返回sub_openid)
total_amountnumber总金额,大于等于0的数字,单位为分,只能为整数。
prepay_req_bodyobject预下单请求部分输入参数。
注意:商户请求预下单接口时需要使用本对象中同名字段值,若字段在本对象中不存在则由商户根据实际情况填写。
appidstring从业机构在微信公众平台申请服务号对应的AppID,申请商户功能的时候微信支付会配置绑定关系
mchidstring微信支付分配给从业机机构的机构号
sub_appidstringAppID 是商户在微信申请公众号成功后分配的帐号ID,需要机构侧有配置绑定关系才能传
sub_mchidstring从业机构报备的商户返回的商户识别码
channel_idstring微信支付分配给收单服务商的ID
device_infostring终端设备号,PC网页或公众号内支付请传"WEB"
nonce_strstring微信支付分配给收单服务商的ID
bodystring商品或支付单简要描述
attachstring附加数据,在查询API和支付通知中原样返回,该字段主要用于商户携带订单的自定义数据
fee_typestring符合ISO 4217标准的三位字母代码,默认人民币:CNY
time_startstring订单生成时间,格式为yyyyMMddHHmmss
time_expirestring订单失效时间,格式为yyyyMMddHHmmss
goods_tagstring商品标记,代金券或立减优惠功能的参数, 说明详见代金券或立减优惠
notify_urlstring异步接收微信支付结果通知的回调地址,通知URL必须为外网可访问的URL,不能携带参数。请使用HTTPS协议链接
trade_typestringJSAPI:公众号支付
NATIVE:扫码支付
App:App 支付
limit_paystringno_credit:指定不能使用信用卡支付
openidstringtrade_type=JSAPI,此参数必传,用户在主商户AppID下的唯一标识。OpenID和sub_openid可以选传其中之一,如果选择传sub_openid,则必须传sub_appid。
sub_openidstringtrade_type=JSAPI,此参数必传,用户在主商户AppID下的唯一标识。OpenID和sub_openid可以选传其中之一,如果选择传sub_openid,则必须传sub_appid。
need_receiptboolean需要跟公众平台的发票功能联合使用
prepay_req_header_base64stringBase64编码后的从业机构请求预下单接口使用请求的头部
prepay_req_body_base64stringBase64编码后的从业机构请求预下单接口使用请求的包体
prepay_resp_http_codenumber从业机构请求预下单接口时,收到响应的HttpCode
prepay_resp_header_base64stringBase64编码后的从业机构请求预下单接口收到响应的头部
prepay_resp_body_base64stringBase64编码后的从业机构请求预下单接口收到响应的包体
php
function webhookProcessor(\Psr\Http\Message\RequestInterface $request,
  array $platformPublicKeyMap, string $apiv3Key): array {
  if (!\count($platformPublicKeyMap)) {
    throw new \WeChatPay\Exception\InvalidArgumentException('平台证书或者平台公钥数组不能为空');
  }

  if (\strlen($apiv3Key) !== 32) {
    throw new \WeChatPay\Exception\InvalidArgumentException('APIV3密钥为32字节,长度不对');
  }

  if (!($request->hasHeader(\WeChatPay\WechatpayNonce)
    && $request->hasHeader(\WeChatPay\WechatpaySerial)
    && $request->hasHeader(\WeChatPay\WechatpaySignature)
    && $request->hasHeader(\WeChatPay\WechatpayTimestamp))) {
    throw new \WeChatPay\Exception\InvalidArgumentException('通知的头参数缺失必要参数');
  }

  // 检查通知的时间偏移量,允许5分钟之内的偏移
  [$inWechatpayTimestamp] = $request->getHeader(\WeChatPay\WechatpayTimestamp);
  if (\WeChatPay\MAXIMUM_CLOCK_OFFSET < \abs(
    \WeChatPay\Formatter::timestamp() - (int)$inWechatpayTimestamp)) {
    throw new \WeChatPay\Exception\InvalidArgumentException('通知头参数的时间偏移量超过可信阈值');
  }

  // 检查通知的平台证书/平台公钥实例是否存在
  [$inWechatpaySerial] = $request->getHeader(\WeChatPay\WechatpaySerial);
  if (!\array_key_exists($inWechatpaySerial, $platformPublicKeyMap)) {
    throw new \WeChatPay\Exception\InvalidArgumentException('通知头参数的证书序列号/公钥ID本地不存在');
  }

  // 验证通知的数据签名
  [$inWechatpaySignature] = $request->getHeader(\WeChatPay\WechatpaySignature);
  [$inWechatpayNonce] = $request->getHeader(\WeChatPay\WechatpayNonce);
  $inBody = (string)$request->getBody();
  if (!\WeChatPay\Crypto\Rsa::verify(
    \WeChatPay\Formatter::joinedByLineFeed($inWechatpayTimestamp, $inWechatpayNonce, $inBody),
    $inWechatpaySignature, $platformPublicKeyMap[$inWechatpaySerial]
  )) {
    throw new \WeChatPay\Exception\InvalidArgumentException('通知头参数的数据签名校验未通过');
  }

  // 转换通知的JSON文本消息为PHP Array数组
  $inBodyArray = (array)\json_decode($inBody, true);
  // 使用PHP7+的数据解构语法,从Array中解构并赋值变量
  ['resource' => [
    'ciphertext'      => $ciphertext,
    'nonce'           => $nonce,
    'associated_data' => $aad
  ]] = $inBodyArray;
  // 加密文本消息解密,有可能解密异常(eg: 平台探测流量)会抛 \UnexpectedValueException
  $inBodyResource = \WeChatPay\Crypto\AesGcm::decrypt($ciphertext, $apiv3Key, $nonce, $aad);
  // 把解密后的文本转换为PHP Array数组
  $inBodyResourceArray = (array)\json_decode($inBodyResource, true);
  // 把解密后的数组定义为'original_data'函数返回
  $inBodyArray['resource']['original_data'] = $inBodyResourceArray;

  return [
    'headers' => $request->getHeaders(),
    'body' => $inBodyArray
  ];
}

// do your business
// ...
// ...
$json = \json_encode([
  'code' => 'SUCCESS',
  'message' => 'OK'
]);
应答规范类型描述
statusnumberHTTP状态码
20X 4XX 5XX 枚举值之一
bodyobject应答的JSON数据结构
codestring业务处理状态码
SUCCESS | FAIL 枚举值之一
messagestring业务处理附加信息

参阅

Published on the GitHub by TheNorthMemory