+1(469)278-6367 bill@vmshell.com

VmShell Pay 商户接入 API 文档

下游商户统一调用 VmShell Pay,本系统完成应用审核、签名验证、Heepay 上游下单、回调入账、余额同步与商户 Webhook 通知。

API Version v1

一、接入总览

VmShell Pay 采用支付中台模式。商户在前台提交应用并通过后台审核后,可获得 AppIdAppSecret。商户系统只需要调用本平台 API,本平台会按已启用通道向 Heepay CrossPay 发起上游支付请求,并在收到上游支付或退款回调后更新商户钱包余额、平台差价收益、订单状态与争议数据。

1. 创建应用
2. 后台审核
3. 商户签名下单
4. 平台转发 Heepay
5. 平台回调商户
环境网关地址说明
正式支付https://www.vmshell.win/api/v1/pay.php创建中台交易并调用 Heepay 上游下单。
正式查单https://www.vmshell.win/api/v1/query.php查询平台订单状态,可同步 Heepay 上游状态。
沙箱测试https://www.vmshell.win/api/sandbox/test_pay.php仅创建测试订单,不触发真实扣款。

二、Heepay 上游通道映射

商户侧无需直接保管 Heepay 大商户密钥。Heepay 商户号、AppId、MD5 Key 与 pay_secret 由平台后台统一管理,当前上游配置摘要如下,敏感字段已遮罩。

配置项当前值
上游商户号 mchNoM1779761372
上游 AppId6a1500dce4b0467841318266
签名方式MD5
MD5 Keycsksh6************************yltf
Pay Secret611e5f**********************4ff4
上游网关https://crosspay.heepay.com/pay

如商户需要透传 Heepay 支付扩展字段,可在下单请求中传入 way_codeif_codeclient_ipsubjectbodyext_param 等字段;平台会在风控与审核允许的前提下映射为上游参数。

三、签名认证

每个商户应用拥有独立 AppSecret。请求参数按键名升序排序,过滤空值、数组与 sign 字段后拼接为 key=value 串,最后追加 key=AppSecret 并计算小写 MD5。平台会验证签名、应用审核状态、关停状态、回调地址与可用通道。

sign = md5(app_id=APP_123&amount=10.00&currency=USD&order_id=ORDER_10001&timestamp=1710000000&key=APP_SECRET)
认证字段是否必填说明
app_id商户应用 ID,来自“我的应用”。
timestamp建议Unix 时间戳,建议服务端校验 5 分钟有效期。
nonce建议随机字符串,用于防重放。
sign按 MD5_KSORT_KEY 算法生成。

四、支付下单 API

POST https://www.vmshell.win/api/v1/pay.php
参数类型必填说明
app_idstring商户应用 ID。
order_idstring商户唯一订单号。
amountdecimal订单金额,单位为主币,例如 12.50。
currencystring默认 USD。
payment_methodstringalipaywechat,默认按平台通道策略选择。
subjectstring商品标题,会映射到 Heepay subject。
bodystring商品描述,会映射到 Heepay body。
notify_urlurl平台完成支付、退款、争议处理后通知商户的地址。
return_urlurl用户支付完成后跳转地址。
client_ipstring买家 IP,可透传至 Heepay。
ext_paramstring/json商户扩展参数,平台回调时原样返回。
signstring请求签名。
{ "code": 0, "message": "success", "data": { "transaction_id": "VMP_TX_202605280001", "order_id": "ORDER_10001", "pay_url": "https://crosspay.heepay.com/pay?...", "status": "processing", "platform_fee": "0.18", "merchant_net_amount": "9.82" } }

五、订单查询 API

GET/POST https://www.vmshell.win/api/v1/query.php

商户可使用平台交易号或商户订单号查询订单。若请求中带有 sync_upstream=1,平台会调用 Heepay 查单能力同步上游状态,并写入查询日志。

参数必填说明
app_id商户应用 ID。
order_id二选一商户订单号。
transaction_id二选一平台交易号。
sync_upstream是否同步 Heepay 上游,1 为同步。
sign签名。

六、商户 Webhook 回调

Heepay 上游通知先进入平台的 https://www.vmshell.win/api/heepay_notify.php 或退款通知 https://www.vmshell.win/api/heepay_refund_notify.php。平台完成验签、入账、差价记录、退款同步或争议裁决后,再调用商户应用配置的 notify_url

字段说明
eventpayment.completedpayment.failedrefund.completeddispute.resolved
transaction_id平台交易号。
order_id商户订单号。
amount订单金额。
merchant_net_amount扣除平台费率后的商户净入账。
status订单状态。
heepay_pay_order_idHeepay 上游订单号,如上游已返回。
sign平台使用商户 AppSecret 生成的回调签名。

七、多语言示例代码

PHP

<?php $params = [ 'app_id' => 'YOUR_APP_ID', 'order_id' => 'ORDER_' . time(), 'amount' => '10.00', 'currency' => 'USD', 'subject' => 'Demo Order', 'notify_url' => 'https://merchant.example.com/pay/notify', 'timestamp' => time() ]; ksort($params); $pairs = []; foreach ($params as $k => $v) { if ($v !== '' && $v !== null) $pairs[] = $k . '=' . $v; } $params['sign'] = strtolower(md5(implode('&', $pairs) . '&key=YOUR_APP_SECRET')); $ch = curl_init('https://www.vmshell.win/api/v1/pay.php'); curl_setopt_array($ch, [CURLOPT_POST => true, CURLOPT_POSTFIELDS => http_build_query($params), CURLOPT_RETURNTRANSFER => true]); echo curl_exec($ch);

Node.js

const crypto = require('crypto'); const axios = require('axios'); const params = { app_id:'YOUR_APP_ID', order_id:'ORDER_' + Date.now(), amount:'10.00', currency:'USD', subject:'Demo Order', notify_url:'https://merchant.example.com/pay/notify', timestamp:Math.floor(Date.now()/1000) }; const base = Object.keys(params).sort().map(k => `${k}=${params[k]}`).join('&'); params.sign = crypto.createHash('md5').update(base + '&key=YOUR_APP_SECRET').digest('hex').toLowerCase(); axios.post('https://www.vmshell.win/api/v1/pay.php', params).then(r => console.log(r.data));

Python

import hashlib, time, requests params = {'app_id':'YOUR_APP_ID','order_id':f'ORDER_{int(time.time())}','amount':'10.00','currency':'USD','subject':'Demo Order','notify_url':'https://merchant.example.com/pay/notify','timestamp':str(int(time.time()))} base = '&'.join([f'{k}={params[k]}' for k in sorted(params.keys()) if params[k]]) params['sign'] = hashlib.md5((base + '&key=YOUR_APP_SECRET').encode()).hexdigest().lower() print(requests.post('https://www.vmshell.win/api/v1/pay.php', data=params).json())

Java

Map<String, String> p = new TreeMap<>(); p.put("app_id", "YOUR_APP_ID"); p.put("order_id", "ORDER_10001"); p.put("amount", "10.00"); p.put("currency", "USD"); p.put("subject", "Demo Order"); String base = p.entrySet().stream().map(e -> e.getKey() + "=" + e.getValue()).collect(Collectors.joining("&")); String sign = DigestUtils.md5Hex(base + "&key=YOUR_APP_SECRET").toLowerCase(); // 使用 HttpClient POST 至 https://www.vmshell.win/api/v1/pay.php

Go

params := map[string]string{"app_id":"YOUR_APP_ID","order_id":"ORDER_10001","amount":"10.00","currency":"USD","subject":"Demo Order"} keys := make([]string, 0, len(params)); for k := range params { keys = append(keys, k) }; sort.Strings(keys) parts := []string{}; for _, k := range keys { parts = append(parts, k+"="+params[k]) } sum := md5.Sum([]byte(strings.Join(parts, "&") + "&key=YOUR_APP_SECRET")) params["sign"] = hex.EncodeToString(sum[:]) // http.PostForm("https://www.vmshell.win/api/v1/pay.php", url.Values{...})

八、错误码

错误码说明建议处理
0成功。读取 data 并继续业务处理。
40001缺少必要参数。检查 app_id、order_id、amount、notify_url 与 sign。
40002签名验证失败。确认排序规则、空值过滤和 AppSecret。
40003应用未审核或已关停。进入“我的应用”查看审核状态。
40004订单重复。确保同一 app_id 下 order_id 唯一。
50001上游 Heepay 调用失败。稍后重试或联系平台管理员查看上游日志。
50002风控拦截。降低频率、补充订单信息或提交工单。