對接文件
本頁還沒有繁體中文版本,下面是简体中文內容。

SDK 下载

代付接口 std1 有六种语言的 SDK:Java、PHP、Python、Go、Node.js、.NET。六种行为完全一样:同一组参数发出同样的字节,同一个应答判成同样的结果、给出同样的提示。都没有写死的地址,接口地址、商户号、密钥由您配置。代收接口 compat1(老协议)没有 SDK。

下载

语言最低版本文件大小SHA-256
JavaJava 8std1-sdk-java-1.0.0.zip89 KB6e5c3980a03f04cd48968b6425159f5110b1a0171fd89769fef7b342d91e8c4c
PHPPHP 7.4std1-sdk-php-1.0.0.zip93 KB340dfd49cc9c89e90aea527fbe91eaa5de39b5c694ee20bc6fce0902a589ee12
PythonPython 3.7std1-sdk-python-1.0.0.zip63 KBdec28311467ae757b22299e6f9d4e40db9cf0134f41c0b95f3ad84d2ad836d47
GoGo 1.18std1-sdk-go-1.0.0.zip82 KBd3e680e4fe1f85d511319d90016e7c233caab934f72377753b15b2eadff628fc
Node.js(TypeScript)Node.js 16std1-sdk-node-1.0.0.zip120 KB9dafafaafaf666cc404149596e56d3ca6bb561e9344f2f696472fbf5ac86d327
.NET.NET Standard 2.0std1-sdk-dotnet-1.0.0.zip99 KB3e322b359bcb2800303166022d21c924cc72f7690e3a0078b626ac001ca99fe8
全部六种—std1-sdk-all-1.0.0.zip398 KB79097c0dd230e8638c732f309049bc3612ba3ab5ef4868795d2ee896a29f2fc1

配置

结果只有五种

结果意思您该做什么
SUCCESS提代付=已受理、钱已冻结,不是已出款;查单=查到了,看 status;查余额=拿到了等回调或查单
NOT_PROCESSED本次没被处理(参数错、签名错、IP 未授权、限流、商户号不对)改正后用原单号重发
REJECTED订单已拒绝(终态),该商户订单号已占用(MERCHANT_DISABLED「商户未开通代付接口」不占用,开通后可使用原商户订单号重新提交)如需代付,请使用新的商户订单号提交
UNKNOWN结果不明(超时、断线、HTTP 不是 200、验签不过、系统繁忙、单号冲突)用同一单号、完全相同的参数重试或查单
NOT_FOUND查单查不到不能据此判断没建单

示例

下面的示例取自各语言包里的 README。

Java

提代付

ApplyRequest request = ApplyRequest.builder()
        .mchOrderNo("ORDER-20260917-0001")          // 你的单号,同商户唯一;结果不明时重试只能用它
        .amountCents(Amounts.yuanToCents("19.99"))  // "1999";也可以 amountCents(1999L)
        .accountName("张三")
        .accountNo("6222021234567890123")
        .bankName("中国工商银行")
        .notifyUrl("https://你的回调地址/std1/notify") // 可选;不给就只能靠查单
        .remark("备注")                              // 可选,查单和回调原样带回
        .extra("myTag", "vip")                       // 可选:自带字段,照签照发、网关忽略;同一个名字只能设一次
        .extra("productId", "2")                     // 可选:代付产品编号,用自带字段带;不带走这户的默认代付产品
        .build();

Std1Result result = client.applyPayout(request);
switch (result.getOutcome()) {
    case SUCCESS:        // 已受理、钱已冻结,还不是已出款;记下 result.getPayoutNo(),等回调或查单
        break;
    case REJECTED:       // 订单已拒绝(终态),该商户订单号已占用;如需代付,请使用新的商户订单号
        break;
    case NOT_PROCESSED:  // 本次没处理:按 result.getHint() 改正后,用原单号重发
        break;
    default:             // UNKNOWN:结果不明——用同一单号、完全相同的参数重试或查单,不要换单号重下
        break;
}
System.out.println(result.getOutcome() + " " + result.getHint());   // 提示有中文 getHint() 和英文 getHintEn()

收回调

protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
    byte[] body = readAll(req.getInputStream());          // 只读正文字节,别碰 getParameter*
    try {
        PayoutNotification n = client.parseNotification(body);
        saveIfNewer(n.getPayoutNo(), n.getStatus(), n);    // 你的落库逻辑(按 payoutNo+status 去重)
        resp.setStatus(200);
        resp.getWriter().write(Std1Client.SUCCESS_REPLY);
    } catch (InvalidNotificationException e) {
        resp.setStatus(400);                               // e.getReason(): malformed / signature / merchant / fields
        resp.getWriter().write("fail");
    }
}

PHP

提代付

use Std1\Payout\Amounts;
use Std1\Payout\ApplyRequest;
use Std1\Payout\Outcome;

$request = new ApplyRequest([
    'mchOrderNo'  => 'A20260917000001',                  // 商户单号,同一商户唯一,就是防重复的键
    'amount'      => Amounts::yuanToCents('100.00'),     // '10000' 分。不要写 100.00 * 100
    'accountName' => '张三',
    'accountNo'   => '6222021234567890123',
    'bankName'    => '中国工商银行',
    'notifyUrl'   => 'https://商户回调地址/std1/notify',   // 可选;不传就不发回调,只能查单
    'remark'      => '九月工资',                          // 可选;查单、回调原样带回
], [
    'myBatch' => '0917',                                  // 可选:你自己的字段,会签名、网关忽略
    'productId' => '2',                                   // 可选:代付产品编号,用自带字段带;不带走这户的默认代付产品
]);

// 发之前先把这组参数和单号一起存库:结果不明时要原样重发
$result = $client->applyPayout($request);

switch ($result->getOutcome()) {
    case Outcome::SUCCESS:        // 已受理、钱已冻结,还没出款;等回调或查单
        saveAccepted($request->getMchOrderNo(), $result->getPayoutNo());
        break;
    case Outcome::REJECTED:       // 订单已拒绝(终态),该商户订单号已占用;如需代付,请使用新的商户订单号
        markRejected($request->getMchOrderNo(), $result->getCode(), $result->getMessage());
        break;
    case Outcome::NOT_PROCESSED:  // 本次没处理:按提示改正后,用原单号重发
        logWarning($result->getHint());
        break;
    default:                      // UNKNOWN 结果不明:用同一单号、完全相同的参数重试或查单,绝不换单号
        scheduleRetryWithSameParams($request->getMchOrderNo(), $result->getHint());
}

收回调

use Std1\Payout\Client;
use Std1\Payout\InvalidNotificationException;
use Std1\Payout\Statuses;

$raw = file_get_contents('php://input');      // 只用正文原文。绝不用 $_POST、$_REQUEST
try {
    $notification = $client->parseNotification($raw);
} catch (InvalidNotificationException $e) {
    http_response_code(400);                  // 不合法:不要回 success
    echo 'fail';
    exit;
}

// 先落库,再回 success:按 payoutNo + status 去重;只接受往后走的状态
$previous = findStatus($notification->getPayoutNo());          // 没记过为 null
if (Statuses::isNewer($previous, $notification->getStatus())) {
    saveStatus($notification->getPayoutNo(), $notification->getStatus(), $notification->getFailMsg());
}
echo Client::SUCCESS_REPLY;                   // 正文只能是 success,前后不要有任何输出

Python

提代付

amount = std1.yuan_to_cents("19.99")      # "1999":主单位转最小单位只收字符串,不经过浮点

request = std1.ApplyRequest(
    mch_order_no="ORDER-20260917-0001",    # 你的单号:同一笔钱永远用同一个单号
    amount=amount,                         # 最小单位整数:str 或 int,浮点直接报错
    account_name="张三",
    account_no="6222021234567890123",
    bank_name="中国工商银行",
    notify_url="https://你的域名/std1/notify",   # 可选
    remark="备注",                          # 可选;空串和 None 等于没传
    extra={"productId": "2"},              # 可选:代付产品编号,用自带字段带;不带走这户的默认代付产品
)
result = client.apply_payout(request)

if result.outcome == std1.Outcome.SUCCESS:
    save_payout_no(result.field("payoutNo"))   # 已受理、钱已冻结,还不是已出款
elif result.outcome == std1.Outcome.REJECTED:
    mark_rejected(result.code, result.message)   # 订单已拒绝(终态),该商户订单号已占用;如需代付,请使用新的商户订单号
elif result.outcome == std1.Outcome.NOT_PROCESSED:
    fix_and_retry_same_order_no(result.hint)     # 本次没处理:改正后用原单号重发
else:  # UNKNOWN
    retry_later_with_same_params(result.hint)    # 结果不明:同一单号、完全相同的参数重试或查单

收回调

# Flask
@app.post("/std1/notify")
def std1_notify():
    try:
        notification = client.parse_notification(request.get_data())   # 原始正文字节
    except std1.InvalidNotification:
        return "fail", 400                        # 不合法:不要回 success
    save_once(notification.payout_no, notification.status, notification)   # 先落库(按 payoutNo+status 去重)
    return std1.SUCCESS_REPLY, 200               # 再回 success

Go

提代付

cents, err := std1payout.YuanToCents("19.99") // "1999"
if err != nil { /* 金额写法不对,最多两位小数 */ }

result, err := client.ApplyPayout(ctx, std1payout.ApplyRequest{
    MchOrderNo:  "ORDER-20260917-0001", // 你的单号,同一商户唯一
    Amount:      cents,
    AccountName: "张三",
    AccountNo:   "6222021234567890123",
    BankName:    "中国工商银行",
    NotifyURL:   "https://你的回调地址/std1/notify", // 可选
    Remark:      "备注",                           // 可选
    Extra:       []std1payout.Param{
        {Name: "myTag", Value: "vip"},   // 可选:你自己的字段,会参与签名
        {Name: "productId", Value: "2"}, // 可选:代付产品编号,用自带字段带;不带走这户的默认代付产品
    },
})
if err != nil {
    // 只有参数本身不对才会走到这里(缺必填、金额不是最小单位整数、自带字段重名),这时什么都没发出去
}
switch result.Outcome {
case std1payout.OutcomeSuccess:
    // 已受理、钱已冻结,还不是已出款;结果等回调或查单。result.Field("payoutNo") 是平台代付单号
case std1payout.OutcomeRejected:
    // 已拒绝(result.Code 说明原因):订单已拒绝(终态),该商户订单号已占用;如需代付,请使用新的商户订单号
case std1payout.OutcomeNotProcessed:
    // 本次没处理(参数、IP、签名、限流):按 result.Hint 改正后用原单号重发
default: // OutcomeUnknown
    // 结果不明:用同一单号、完全相同的参数重试或查单,不要换单号重下
}

收回调

http.Handle("/std1/notify", client.NotificationHandler(func(n *std1payout.Notification) error {
    // 1. 按 n.PayoutNo + n.Status 去重;只接受往后走的状态:std1payout.IsNewerStatus(已记录的状态, n.Status)
    // 2. 先把结果落库
    // 3. 返回 nil,SDK 回 HTTP 200「success」;返回 error,SDK 回 500,网关稍后重发
    return saveResult(n)
}))

Node.js(TypeScript)

提代付

const result = await client.applyPayout({
  mchOrderNo: 'ORDER-20260917-0001',   // 商户单号:重试必须同一个单号、完全相同的参数
  amount: yuanToCents('100.00'),        // 金额只收最小单位整数:'10000'、10000 或 10000n
  accountName: '张三',
  accountNo: '6222021234567890123',
  bankName: '中国工商银行',
  bankBranch: null,                     // 可选:bankBranch、bankProvince、bankCity、currency(只收本部署币种,现在是 CNY)
  notifyUrl: 'https://你的回调地址/std1/notify',
  remark: '工资',
  extra: [
    ['myTraceId', 'T-1'],               // 可选:自带字段(参与签名,网关验签后忽略)
    ['productId', '2'],                 // 可选:代付产品编号,用自带字段带;不带走这户的默认代付产品
  ],
});

switch (result.outcome) {
  case Outcome.SUCCESS:        // 已受理、钱已冻结,还不是已出款;等回调或查单
    save(result.field('payoutNo'));
    break;
  case Outcome.REJECTED:       // 订单已拒绝(终态),该商户订单号已占用;如需代付,请使用新的商户订单号
    markRejected(result.code, result.message);
    break;
  case Outcome.NOT_PROCESSED:  // 本次没处理(参数、IP、签名、限流);改正后用原单号重发
    fixAndRetrySameOrderNo(result.code, result.message);
    break;
  default:                     // UNKNOWN:结果不明;用同一单号、完全相同的参数重试或查单,绝不换单号
    retryLaterSameParams();
}
console.log(result.hint); // 中文提示;result.hintEn 是英文

收回调

const express = require('express');
const app = express();

// 只给回调这个路由用 raw,拿原始 Buffer;不要用 express.urlencoded() 解析后的对象,更不要把 req.query 合进来
app.post('/std1/notify', express.raw({ type: '*/*', limit: '64kb' }), async (req, res) => {
  let notification;
  try {
    notification = client.parseNotification(req.body); // Buffer
  } catch (error) {
    // InvalidNotificationError:error.reason 是 malformed / signature / merchant / fields
    return res.status(400).type('text/plain').send('fail'); // 不要回 success,网关会重发
  }
  // 先落库再回 success;按 payoutNo + status 去重;只接受往后走的状态
  await saveOnce(notification.payoutNo, notification.status, notification);
  res.status(200).type('text/plain').send(SUCCESS_REPLY);
});

.NET

提代付

var request = new ApplyRequest
{
    MchOrderNo = "ORDER-20260917-0001",       // 你系统里的单号:同一商户唯一,就是幂等键
    Amount = Amounts.YuanToCents("100.00"),   // "10000",金额一律是最小单位整数;最小单位整数也可以用 request.SetAmountCents(10000)
    AccountName = "张三",
    AccountNo = "6222021234567890123",
    BankName = "中国工商银行",
    NotifyUrl = "https://回调地址/std1/notify", // 可选;不传就不发回调,只能查单
    Remark = "工资",                           // 可选;查单、回调原样带回
};
request.AddExtra("myTag", "vip");             // 可选:自带字段参与签名,网关验签后忽略
request.AddExtra("productId", "2");           // 可选:代付产品编号,用自带字段带;不带走这户的默认代付产品

// 先把单号和这组参数存库:结果不明时要原样重发
Std1Result result = await client.ApplyPayoutAsync(request);
switch (result.Outcome)
{
    case Outcome.Success:       // 已受理、钱已冻结,还不是已出款:等回调或查单
        SavePayoutNo(request.MchOrderNo, result.PayoutNo);
        break;
    case Outcome.Rejected:      // 订单已拒绝(终态),该商户订单号已占用;如需代付,请使用新的商户订单号
        MarkRejected(request.MchOrderNo, result.Code, result.Message);
        break;
    case Outcome.NotProcessed:  // 本次没被处理:改正原因后用原单号重发
        Alert(result.Hint);
        break;
    default:                    // Unknown:结果不明:用原单号、原参数重发或查单,绝不换单号
        ScheduleRetryOrQuery(request.MchOrderNo);
        break;
}

收回调

app.MapPost("/std1/notify", async (HttpRequest http) =>
{
    byte[] body;
    using (var buffer = new MemoryStream())
    {
        await http.Body.CopyToAsync(buffer);   // 原始正文字节
        body = buffer.ToArray();
    }
    PayoutNotification n;
    try
    {
        n = client.ParseNotification(body);
    }
    catch (InvalidNotificationException e)       // e.Reason:malformed/signature/merchant/fields
    {
        Console.Error.WriteLine("std1 回调不合法:" + e.Reason);
        return Results.Text("fail", statusCode: 400);
    }
    await SaveIfNewerAsync(n);                   // 按 PayoutNo+Status 去重,只收往后走的状态
    return Results.Text(Std1Client.SuccessReply);
});