Skip to content
赞助

微信小程序虚拟支付

虚拟支付配置请参考 初始化 - 微信配置 中的 virtual_pay 部分

微信小程序虚拟支付支持小程序内虚拟商品(如游戏代币、会员订阅等)的支付能力。

方法名参数返回值
virtualarray $orderCollection
successarray $paramsResponse

WARNING

虚拟支付必须配置 mini_app_id,因为虚拟支付仅支持小程序场景。

客户端签名(代币充值)

客户端签名用于小程序端调起虚拟支付,SDK 会生成签名数据供前端使用,不会发送 HTTP 请求

例子

php
Pay::config($config);

$order = [
    'buyQuantity' => 100,
    'productId' => 'product_001',
    'goodsPrice' => 1,
    'outTradeNo' => time().'',
    'currencyType' => 'CNY', // 可选,默认 CNY
    'attach' => '自定义数据', // 可选
];

$result = Pay::wechat()->virtual($order);
// 返回 Collection 实例,包含以下字段:
// $result->offerId       - 商户号
// $result->buyQuantity   - 购买数量
// $result->currencyType  - 货币类型
// $result->productId     - 商品 ID
// $result->goodsPrice    - 商品价格(分)
// $result->outTradeNo    - 商户订单号
// $result->paySig        - 签名
// $result->signData      - 与签名逐字节一致的 JSON 字符串,请原样传递给 wx.requestVirtualPayment
// $result->env           - 环境(0=正式,1=沙箱)

订单配置参数

参数必填说明
buyQuantity购买数量
productId商品 ID
goodsPrice商品价格(单位:分)
outTradeNo商户订单号
currencyType货币类型,默认 CNY
attach自定义数据
env环境:0=正式(默认),1=沙箱

TIP

客户端签名场景下,SDK 返回签名数据后,需要在小程序端调用 wx.requestVirtualPayment() 完成支付。

使用沙箱环境

php
$order = [
    'buyQuantity' => 100,
    'productId' => 'product_001',
    'goodsPrice' => 1,
    'outTradeNo' => time().'',
    'env' => 1, // 沙箱环境
];

$result = Pay::wechat()->virtual($order);

WARNING

  • paySig 计算公式:HMAC-SHA256(uri + '&' + signData 的 JSON 字符串, app_key)
  • 微信 Midas 验签服务端会按字典序(key 顺序)重组 signData,对字段顺序敏感
  • SDK 已自动将业务字段按字典序排序,并在返回结果中提供与签名计算所用 body 逐字节一致signData JSON 字符串
  • 优先原样传递wx.requestVirtualPayment({ signData: result.signData, ... }),切勿自行 JSON.parse / JSON.stringify 重新序列化(会破坏字段顺序导致验签失败)
  • 如确需自行构造对象:字段名、值、顺序必须与 SDK 返回结果完全一致
  • offerId 必须为字符串类型(数字类型会导致验签失败)

传递 session_key

如果需要验证用户身份,可以传递 _session_key 参数:

php
$order = [
    'buyQuantity' => 100,
    'productId' => 'product_001',
    'goodsPrice' => 1,
    'outTradeNo' => time().'',
    '_session_key' => '用户的 session_key',
];

$result = Pay::wechat()->virtual($order);
// 返回结果中会额外包含 signature 字段

服务端 API

服务端 API 用于查询、退款等操作,SDK 会发送 HTTP 请求到微信服务器。

access_token 自动获取

  • 配置 virtual_pay.app_secret 后,SDK 自动通过微信 stable_token 接口获取并缓存 access_token(进程内缓存,过期前 300 秒提前刷新;多进程部署时各进程独立获取)
  • 参数中手动传入的 access_token 优先于自动获取
  • 微信侧需配置 IP 白名单,否则报 40164 错误
  • 第三方平台代调(使用 authorizer_access_token)不支持自动获取,必须手动传入 access_token
  • 风险提示:开启 Artful 日志(logger.enable = true)时,token 请求的 body 含 app_secret,会随日志落盘,生产环境注意日志安全

查询用户代币余额

php
$order = [
    'openid' => '用户的 openid',
    'env' => 0, // 0=正式,1=沙箱
];

$result = Pay::wechat()->virtual([
    '_action' => 'currency_query_balance',
    ...$order,
]);

代币扣减

php
$order = [
    'openid' => '用户的 openid',
    'user_ip' => '用户 IP',
    'amount' => 100,
    'order_id' => '订单号',
    'payitem' => '道具信息',
    'remark' => '备注',
];

$result = Pay::wechat()->virtual([
    '_action' => 'currency_pay',
    ...$order,
]);

赠送代币

php
$order = [
    'openid' => '用户的 openid',
    'amount' => 100,
    'order_id' => '订单号',
    'remark' => '备注',
];

$result = Pay::wechat()->virtual([
    '_action' => 'currency_present',
    ...$order,
]);

撤销代币扣减

php
$order = [
    'openid' => '用户的 openid',
    'order_id' => '订单号',
];

$result = Pay::wechat()->virtual([
    '_action' => 'currency_cancel',
    ...$order,
]);

查询订单

php
$order = [
    'openid' => '用户的 openid',
    'env' => 0,
    'order_id' => '商户订单号', // 与 wx_order_id 二选一
    // 'wx_order_id' => '微信订单号',
];

$result = Pay::wechat()->virtual([
    '_action' => 'order_query',
    ...$order,
]);

退款

php
$order = [
    'openid' => '用户的 openid',
    'order_id' => '商户订单号', // 与 wx_order_id 二选一
    'refund_order_id' => '退款单号',
    'left_fee' => 100,
    'refund_fee' => 50,
    'refund_reason' => '用户申请退款',
    'req_from' => '退款来源',
];

$result = Pay::wechat()->virtual([
    '_action' => 'order_refund',
    ...$order,
]);

通知发货

php
$order = [
    'order_id' => '商户订单号', // 与 wx_order_id 二选一
];

$result = Pay::wechat()->virtual([
    '_action' => 'order_notify_provide_goods',
    ...$order,
]);

批量上传道具

php
$order = [
    'group_id' => '分组 ID',
    'goods_list' => [
        ['goods_id' => 'goods_001', 'goods_name' => '道具名称', ...],
    ],
];

$result = Pay::wechat()->virtual([
    '_action' => 'goods_start_upload',
    ...$order,
]);

查询上传道具结果

php
$order = [
    'group_id' => '分组 ID',
];

$result = Pay::wechat()->virtual([
    '_action' => 'goods_query_upload',
    ...$order,
]);

批量发布道具

php
$order = [
    'group_id' => '分组 ID',
];

$result = Pay::wechat()->virtual([
    '_action' => 'goods_start_publish',
    ...$order,
]);

查询发布道具结果

php
$order = [
    'group_id' => '分组 ID',
];

$result = Pay::wechat()->virtual([
    '_action' => 'goods_query_publish',
    ...$order,
]);

预通知扣款(订阅)

php
$order = [
    'openid' => '用户的 openid',
    'contract_id' => '合约 ID',
    'pre_payment_amount' => 100,
];

$result = Pay::wechat()->virtual([
    '_action' => 'subscribe_send_pre_payment',
    ...$order,
]);

确认扣款(订阅)

php
$order = [
    'openid' => '用户的 openid',
    'contract_id' => '合约 ID',
    'pre_payment_id' => '预支付单号',
    'pre_payment_amount' => 100,
];

$result = Pay::wechat()->virtual([
    '_action' => 'subscribe_submit_pay_order',
    ...$order,
]);

查询订阅合约

php
$order = [
    'openid' => '用户的 openid',
    'contract_id' => '合约 ID',
];

$result = Pay::wechat()->virtual([
    '_action' => 'subscribe_query_contract',
    ...$order,
]);

取消订阅合约

php
$order = [
    'openid' => '用户的 openid',
    'contract_id' => '合约 ID',
];

$result = Pay::wechat()->virtual([
    '_action' => 'subscribe_cancel_contract',
    ...$order,
]);

创建提现单

php
$order = [
    'withdraw_no' => '提现单号',
    'withdraw_amount' => 100, // 选填
];

$result = Pay::wechat()->virtual([
    '_action' => 'withdraw_create',
    ...$order,
]);

查询提现单

php
$order = [
    'withdraw_no' => '提现单号',
];

$result = Pay::wechat()->virtual([
    '_action' => 'withdraw_query',
    ...$order,
]);

查询商户余额

php
$result = Pay::wechat()->virtual([
    '_action' => 'withdraw_query_balance',
]);

下载账单

php
$order = [
    'bill_date' => '2024-01-01',
    'bill_type' => 'ALL',
];

$result = Pay::wechat()->virtual([
    '_action' => 'order_download_bill',
    ...$order,
]);

查询下载结果

php
$order = [
    'download_bill_id' => '下载单号',
];

$result = Pay::wechat()->virtual([
    '_action' => 'order_query_download',
    ...$order,
]);

接收回调

虚拟支付回调使用与普通微信回调不同的签名和加密方式。

例子

php
Pay::config($config);

$result = Pay::wechat()->callback(null, ['_action' => 'virtual']);
// 返回 Collection 实例,包含解密后的回调数据

WARNING

虚拟支付回调必须传递 _action => 'virtual' 参数,否则会使用 V3 回调插件导致验签失败。

回调事件类型

虚拟支付回调包含以下事件类型:

事件说明
xpay_goods_deliver_notify道具发货通知
xpay_coin_pay_notify代币支付通知
xpay_refund_notify退款通知

回调数据示例

php
$result = Pay::wechat()->callback(null, ['_action' => 'virtual']);

// 通用字段
$result->ToUserName;  // 公众号/小程序原始 ID
$result->FromUserName; // 用户 openid
$result->CreateTime;   // 创建时间
$result->MsgType;      // 消息类型(event)
$result->Event;        // 事件类型

// 业务字段(根据事件类型不同)
$result->OrderKey;     // 订单信息(代币支付通知)
$result->RefundId;     // 退款单号(退款通知)

确认回调

方法名参数返回值
successarrayResponse

例子

php
Pay::config($config);

$result = Pay::wechat()->callback(null, ['_action' => 'virtual']);

// 处理业务逻辑...

return Pay::wechat()->success(['_action' => 'virtual']);

响应格式

默认返回 XML 格式:

xml
<xml>
  <ErrCode>0</ErrCode>
  <ErrMsg>success</ErrMsg>
</xml>

如果需要返回 JSON 格式:

php
return Pay::wechat()->success(['_action' => 'virtual', '_format' => 'json']);
// {"ErrCode":0,"ErrMsg":"success"}

TIP

微信虚拟支付回调的响应格式与普通支付不同,使用 ErrCode/ErrMsg 而非 code/message

Released under the MIT License.