N支付 开发文档

微信Native支付中转服务 API v1.1

📑 目录

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节:指定子商户号详解

2. 签名规则

所有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分钟,超时请求将被拒绝。

3. 统一下单

POST/api/pay/order
参数类型必填说明
app_idstring必填商户AppID
timestampstring必填当前时间戳(秒)
signstring必填MD5签名
out_trade_nostring必填商户订单号(唯一)
amountnumber必填金额(元,如100.00)
descriptionstring可选商品描述
notify_urlstring可选支付成功回调地址
wechat_config_idnumber可选指定微信商户号配置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
  }
}
💡 pay_url 是消费者支付链接,直接打开即可看到支付页面(含二维码)。商户可将此链接嵌入按钮/短信/邮件等场景。code_url 为微信原始支付链接,商户也可自行处理。

4. 查询订单

POST/api/pay/query
参数类型必填说明
app_idstring必填商户AppID
timestampstring必填时间戳
signstring必填MD5签名
order_nostring二选一系统订单号
out_trade_nostring二选一商户订单号

5. 申请退款

POST/api/pay/refund
参数类型必填说明
app_idstring必填商户AppID
timestampstring必填时间戳
signstring必填MD5签名
order_nostring二选一系统订单号
out_trade_nostring二选一商户订单号
refund_amountnumber必填退款金额(元)
reasonstring可选退款原因

6. 关闭订单

POST/api/pay/close
参数类型必填说明
app_idstring必填商户AppID
timestampstring必填时间戳
signstring必填MD5签名
order_nostring必填系统订单号

7. 异步通知

支付成功通知

支付成功后,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)。

8. 错误码

code说明
0成功
-1通用错误
401未登录/Token无效
403无权限

9. SDK & 代码示例

Node.js 完整示例

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 完整示例

<?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 发给消费者,或生成二维码展示
}
?>

Python 完整示例

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 发给消费者,或生成二维码展示

Java 完整示例

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);
    }
}

10. 指定子商户号详解 ⭐

什么是子商户号?

一个N支付商户账号可以绑定多个微信商户号(即多套微信支付配置)。例如:

配置ID名称微信商户号状态默认
1主账号1900000101启用✅ 默认
2分店A1900000202启用-
3分店B1900000303启用-

如何使用?

下单时在请求参数中加上 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_id?

  1. 登录 商户后台
  2. 进入「微信配置」页面
  3. 每条配置左侧的 ID 数字就是 wechat_config_id
  4. 如需新增商户号配置,点击「添加配置」并填写微信商户号、APIv3密钥、证书等信息
💡 使用场景举例:
  • 多门店收款:不同门店用不同微信商户号,资金各自独立
  • 分账管理:不同业务线用不同商户号,便于财务对账
  • 服务商模式:帮多个子商户代收,每个子商户一个配置
⚠️ 注意事项:
  • 如果传入的 wechat_config_id 不存在或未启用,系统会回退到默认商户号
  • 每个商户号需要独立配置完整的微信支付证书(私钥+序列号+APIv3密钥)
  • 退款使用的是该订单下单时对应的商户号,无需在退款时再次指定
  • 若商户只有一个微信配置,无需传此参数,系统自动使用默认配置

完整调用流程图

┌─────────────┐     ┌──────────────────┐     ┌─────────────────┐
│  商户系统    │     │   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.收到通知 │     │                  │     │                 │
│   确认收款  │     │                  │     │                 │
└─────────────┘     └──────────────────┘     └─────────────────┘

11. 常见问题

Q: 如何获取app_id和app_secret?

注册商户后在商户端「API密钥」页面查看,或联系管理员创建商户。

Q: 金额单位是什么?

金额单位为,如100.00表示100元。

Q: 订单有效期多久?

订单默认30分钟超时自动关闭,超时后无法支付。

Q: 通知重试机制?

支付成功后通知最多重试5次,间隔递增(30s/1m/5m/10m/30m),商户返回SUCCESS或HTTP 200表示接收成功。

Q: 退款多久到账?

退款由微信支付处理,通常1-3个工作日原路退回。

Q: 不传 wechat_config_id 会怎样?

系统自动使用商户的默认微信配置(商户后台标记为「默认」的那个)。大多数只有一个商户号的商户无需关心此参数。

Q: wechat_config_id 传错了会怎样?

如果该ID不存在或不属于当前商户,系统会自动回退到默认商户号,不会报错。但建议传入正确的ID以确保资金流向正确。

Q: 一个商户最多能配多少个微信商户号?

没有硬性限制,但每个商户号都需要完整配置证书信息才能正常使用。