微信Native支付中转服务 API v1.1
N支付是微信Native支付信息中转系统。商户通过API发起交易,系统调用微信Native支付完成收款,支付成功后异步通知商户系统。
1. 商户系统 → POST /api/pay/order → N支付创建订单 2. N支付 → 微信Native下单 → 返回code_url(支付链接) 3. 消费者 → 扫码支付 → 微信完成收款 4. 微信 → 回调N支付 → N支付更新订单状态 5. N支付 → 回调商户notify_url → 商户系统确认收款
wechat_config_id 参数指定使用哪个商户号收款。详见 第10节:指定子商户号详解。
所有API请求需携带签名参数,用于身份验证和防篡改。
sign = MD5(app_id + app_secret + timestamp)
1. 获取app_id和app_secret(商户后台「API密钥」页面)
2. 获取当前时间戳 timestamp = Math.floor(Date.now() / 1000)
3. 拼接字符串: str = app_id + app_secret + timestamp
4. 计算: sign = MD5(str)
示例:
app_id = "np_abc123"
app_secret = "secret456"
timestamp = "1718179200"
str = "np_abc123secret4561718179200"
sign = MD5("np_abc123secret4561718179200") = "a1b2c3d4..."
⚠️ timestamp容差为5分钟,超时请求将被拒绝。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| app_id | string | 必填 | 商户AppID |
| timestamp | string | 必填 | 当前时间戳(秒) |
| sign | string | 必填 | MD5签名 |
| out_trade_no | string | 必填 | 商户订单号(唯一) |
| amount | number | 必填 | 金额(元,如100.00) |
| description | string | 可选 | 商品描述 |
| notify_url | string | 可选 | 支付成功回调地址 |
| wechat_config_id | number | 可选 | 指定微信商户号配置ID,不传则使用默认商户号 |
{
"app_id": "np_abc123def456",
"timestamp": "1718179200",
"sign": "a1b2c3d4e5f6...",
"out_trade_no": "ORDER_20260612001",
"amount": 100.00,
"description": "测试商品",
"notify_url": "https://your-site.com/callback"
}
{
"app_id": "np_abc123def456",
"timestamp": "1718179200",
"sign": "a1b2c3d4e5f6...",
"out_trade_no": "ORDER_20260612002",
"amount": 50.00,
"description": "指定商户号收款",
"notify_url": "https://your-site.com/callback",
"wechat_config_id": 3
}
{
"code": 0,
"message": "success",
"data": {
"order_no": "N20260612154300001234",
"out_trade_no": "ORDER_20260612001",
"pay_url": "https://n.1.taolianwl.cn/pay/N20260612154300001234",
"code_url": "weixin://wxpay/bizpayurl?pr=xxx",
"amount": "100.00",
"expire_minutes": 30
}
}
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| app_id | string | 必填 | 商户AppID |
| timestamp | string | 必填 | 时间戳 |
| sign | string | 必填 | MD5签名 |
| order_no | string | 二选一 | 系统订单号 |
| out_trade_no | string | 二选一 | 商户订单号 |
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| app_id | string | 必填 | 商户AppID |
| timestamp | string | 必填 | 时间戳 |
| sign | string | 必填 | MD5签名 |
| order_no | string | 二选一 | 系统订单号 |
| out_trade_no | string | 二选一 | 商户订单号 |
| refund_amount | number | 必填 | 退款金额(元) |
| reason | string | 可选 | 退款原因 |
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| app_id | string | 必填 | 商户AppID |
| timestamp | string | 必填 | 时间戳 |
| sign | string | 必填 | MD5签名 |
| order_no | string | 必填 | 系统订单号 |
支付成功后,N支付向商户notify_url发送POST请求:
{
"event": "payment.success",
"order_no": "N20260612154300001234",
"out_trade_no": "ORDER_20260612001",
"amount": "100.00",
"status": "paid",
"wechat_transaction_id": "4200001234202606...",
"paid_at": "2026-06-12T15:43:00.000Z",
"timestamp": "1718179200",
"sign": "MD5(app_id + app_secret + timestamp)"
}
商户需返回 SUCCESS 或 HTTP 200 表示接收成功。
支付成功后通知最多重试5次,间隔递增(30s/1m/5m/10m/30m)。
| code | 说明 |
|---|---|
| 0 | 成功 |
| -1 | 通用错误 |
| 401 | 未登录/Token无效 |
| 403 | 无权限 |
const crypto = require('crypto');
const APP_ID = '你的app_id';
const APP_SECRET = '你的app_secret';
const BASE_URL = 'https://n.1.taolianwl.cn';
// 生成签名
function genSign(timestamp) {
return crypto.createHash('md5')
.update(APP_ID + APP_SECRET + timestamp)
.digest('hex');
}
// 统一下单(不指定商户号,使用默认)
async function createOrder(outTradeNo, amount, description) {
const timestamp = Math.floor(Date.now() / 1000).toString();
const res = await fetch(`${BASE_URL}/api/pay/order`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
app_id: APP_ID,
timestamp,
sign: genSign(timestamp),
out_trade_no: outTradeNo,
amount,
description
})
});
return res.json();
}
// 统一下单(指定子商户号)
async function createOrderWithSubMch(outTradeNo, amount, description, wechatConfigId) {
const timestamp = Math.floor(Date.now() / 1000).toString();
const res = await fetch(`${BASE_URL}/api/pay/order`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
app_id: APP_ID,
timestamp,
sign: genSign(timestamp),
out_trade_no: outTradeNo,
amount,
description,
wechat_config_id: wechatConfigId // 指定微信商户号配置ID
})
});
return res.json();
}
// 使用示例
(async () => {
// 1. 使用默认商户号下单
const order1 = await createOrder('ORDER_001', 100.00, '商品A');
console.log('默认商户号:', order1);
// 2. 指定子商户号下单(wechat_config_id 从商户后台获取)
const order2 = await createOrderWithSubMch('ORDER_002', 50.00, '商品B', 3);
console.log('指定商户号:', order2);
// 3. 拿到 pay_url 后,引导消费者打开支付
if (order2.code === 0) {
console.log('支付链接:', order2.data.pay_url);
// 可以把 pay_url 发给消费者,或生成二维码
}
})();
<?php
$app_id = '你的app_id';
$app_secret = '你的app_secret';
$base_url = 'https://n.1.taolianwl.cn';
// 生成签名
function genSign($appId, $appSecret, $timestamp) {
return md5($appId . $appSecret . $timestamp);
}
/**
* 统一下单
* @param string $outTradeNo 商户订单号
* @param float $amount 金额(元)
* @param string $description 商品描述
* @param int $wechatConfigId 微信商户号配置ID(可选,不传用默认)
*/
function createOrder($outTradeNo, $amount, $description, $wechatConfigId = null) {
global $app_id, $app_secret, $base_url;
$timestamp = time();
$sign = genSign($app_id, $app_secret, $timestamp);
$data = [
'app_id' => $app_id,
'timestamp' => $timestamp,
'sign' => $sign,
'out_trade_no' => $outTradeNo,
'amount' => $amount,
'description' => $description,
];
// 指定子商户号(不传则使用默认商户号)
if ($wechatConfigId !== null) {
$data['wechat_config_id'] = $wechatConfigId;
}
$ch = curl_init($base_url . '/api/pay/order');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
return json_decode($response, true);
}
// ===== 使用示例 =====
// 1. 使用默认商户号下单
$result1 = createOrder('ORDER_' . date('YmdHis'), 100.00, '商品A');
print_r($result1);
// 2. 指定子商户号下单(wechat_config_id=3)
$result2 = createOrder('ORDER_' . date('YmdHis') . '_2', 50.00, '商品B', 3);
print_r($result2);
// 3. 处理返回结果
if ($result2['code'] === 0) {
$payUrl = $result2['data']['pay_url'];
echo "支付链接: " . $payUrl . "\n";
// 将 $payUrl 发给消费者,或生成二维码展示
}
?>
import hashlib
import time
import requests
APP_ID = '你的app_id'
APP_SECRET = '你的app_secret'
BASE_URL = 'https://n.1.taolianwl.cn'
def gen_sign(timestamp):
"""生成MD5签名"""
s = f'{APP_ID}{APP_SECRET}{timestamp}'
return hashlib.md5(s.encode()).hexdigest()
def create_order(out_trade_no, amount, description, wechat_config_id=None):
"""统一下单
Args:
out_trade_no: 商户订单号(唯一)
amount: 金额(元), 如 100.00
description: 商品描述
wechat_config_id: 微信商户号配置ID(可选, 不传用默认)
"""
timestamp = str(int(time.time()))
data = {
'app_id': APP_ID,
'timestamp': timestamp,
'sign': gen_sign(timestamp),
'out_trade_no': out_trade_no,
'amount': amount,
'description': description,
}
# 指定子商户号
if wechat_config_id is not None:
data['wechat_config_id'] = wechat_config_id
resp = requests.post(f'{BASE_URL}/api/pay/order', json=data)
return resp.json()
# ===== 使用示例 =====
# 1. 使用默认商户号下单
result1 = create_order(f'ORDER_{int(time.time())}', 100.00, '商品A')
print('默认商户号:', result1)
# 2. 指定子商户号下单(wechat_config_id=3)
result2 = create_order(f'ORDER_{int(time.time())}_2', 50.00, '商品B', wechat_config_id=3)
print('指定商户号:', result2)
# 3. 处理返回结果
if result2['code'] == 0:
pay_url = result2['data']['pay_url']
print(f'支付链接: {pay_url}')
# 将 pay_url 发给消费者,或生成二维码展示
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.security.MessageDigest;
import java.time.Instant;
public class NPayExample {
private static final String APP_ID = "你的app_id";
private static final String APP_SECRET = "你的app_secret";
private static final String BASE_URL = "https://n.1.taolianwl.cn";
// 生成MD5签名
private static String genSign(String timestamp) throws Exception {
String str = APP_ID + APP_SECRET + timestamp;
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] digest = md.digest(str.getBytes());
StringBuilder sb = new StringBuilder();
for (byte b : digest) {
sb.append(String.format("%02x", b));
}
return sb.toString();
}
// 统一下单
private static String createOrder(String outTradeNo, double amount,
String description, Integer wechatConfigId) throws Exception {
String timestamp = String.valueOf(Instant.now().getEpochSecond());
String sign = genSign(timestamp);
// 构建JSON请求体
StringBuilder json = new StringBuilder();
json.append("{");
json.append("\"app_id\":\"").append(APP_ID).append("\",");
json.append("\"timestamp\":\"").append(timestamp).append("\",");
json.append("\"sign\":\"").append(sign).append("\",");
json.append("\"out_trade_no\":\"").append(outTradeNo).append("\",");
json.append("\"amount\":").append(amount).append(",");
json.append("\"description\":\"").append(description).append("\"");
if (wechatConfigId != null) {
json.append(",\"wechat_config_id\":").append(wechatConfigId);
}
json.append("}");
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(BASE_URL + "/api/pay/order"))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(json.toString()))
.build();
HttpResponse<String> response =
client.send(request, HttpResponse.BodyHandlers.ofString());
return response.body();
}
public static void main(String[] args) throws Exception {
// 1. 使用默认商户号下单
String result1 = createOrder("ORDER_001", 100.00, "商品A", null);
System.out.println("默认商户号: " + result1);
// 2. 指定子商户号下单(wechat_config_id=3)
String result2 = createOrder("ORDER_002", 50.00, "商品B", 3);
System.out.println("指定商户号: " + result2);
}
}
一个N支付商户账号可以绑定多个微信商户号(即多套微信支付配置)。例如:
| 配置ID | 名称 | 微信商户号 | 状态 | 默认 |
|---|---|---|---|---|
| 1 | 主账号 | 1900000101 | 启用 | ✅ 默认 |
| 2 | 分店A | 1900000202 | 启用 | - |
| 3 | 分店B | 1900000303 | 启用 | - |
下单时在请求参数中加上 wechat_config_id 即可:
// 不传 wechat_config_id → 使用默认商户号(上表中ID=1)
{ "app_id": "...", "out_trade_no": "ORDER_001", "amount": 100.00, ... }
// 传 wechat_config_id=3 → 使用分店B的商户号(1900000303)
{ "app_id": "...", "out_trade_no": "ORDER_002", "amount": 50.00, ..., "wechat_config_id": 3 }
wechat_config_idwechat_config_id 不存在或未启用,系统会回退到默认商户号┌─────────────┐ ┌──────────────────┐ ┌─────────────────┐ │ 商户系统 │ │ N支付 API │ │ 微信支付 │ ├─────────────┤ ├──────────────────┤ ├─────────────────┤ │ │ │ │ │ │ │ 1.登录商户 │ │ │ │ │ │ 后台获取 │ │ │ │ │ │ app_id │ │ │ │ │ │ app_secret│ │ │ │ │ │ │ │ │ │ │ │ 2.查看微信 │ │ │ │ │ │ 配置列表 │ │ │ │ │ │ 记下ID=3 │ │ │ │ │ │ │ │ │ │ │ │ 3.调用下单 │────▶│ 4.验签通过 │ │ │ │ 传config │ │ 查到config_id=3│ │ │ │ _id=3 │ │ 使用商户号 │────▶│ 5.Native下单 │ │ │ │ 1900000303 │ │ 返回code_url │ │ │◀────│ 6.返回pay_url │◀────│ │ │ │ │ 和code_url │ │ │ │ 7.将pay_url │ │ │ │ │ │ 发给消费者│ │ │ │ │ │ │ │ │ │ 8.消费者扫码 │ │ │ │ │ │ 完成支付 │ │ │ │ │◀────│ │ │ │ │ 9.微信回调 │ │ │ │ │ │ 更新订单状态 │ │ │ │ │◀────│ 10.异步通知 │ │ │ │ │ │ 商户notify_url │ │ │ │ 11.收到通知 │ │ │ │ │ │ 确认收款 │ │ │ │ │ └─────────────┘ └──────────────────┘ └─────────────────┘
注册商户后在商户端「API密钥」页面查看,或联系管理员创建商户。
金额单位为元,如100.00表示100元。
订单默认30分钟超时自动关闭,超时后无法支付。
支付成功后通知最多重试5次,间隔递增(30s/1m/5m/10m/30m),商户返回SUCCESS或HTTP 200表示接收成功。
退款由微信支付处理,通常1-3个工作日原路退回。
系统自动使用商户的默认微信配置(商户后台标记为「默认」的那个)。大多数只有一个商户号的商户无需关心此参数。
如果该ID不存在或不属于当前商户,系统会自动回退到默认商户号,不会报错。但建议传入正确的ID以确保资金流向正确。
没有硬性限制,但每个商户号都需要完整配置证书信息才能正常使用。