为什么源码交易离不开微信支付?
在源码交易领域,微信支付几乎是最核心的支付方式。国内开发者习惯使用微信扫码或小程序内支付购买源码产品,而支付宝、银联等渠道的使用率远不及微信。对于搭建源码商城的站长来说,稳定、安全地接入微信支付是业务运转的基础。
本文将从实战角度出发,完整讲解在源码交易场景中接入微信支付的全流程:Native 支付(扫码)、JSAPI 支付(公众号/小程序内支付)、支付回调处理、自动发货系统集成,以及日常运维中的常见问题和最佳实践。所有代码基于 PHP + MySQL 环境,并附有 Python 参考实现。
如果你还没有自己的源码商城系统,不妨先看看我们的 源码商城产品,我们提供了完整的商城源码,内置微信支付对接模块,开箱即用。
一、微信支付接入前的准备
1.1 开通微信支付商户平台
首先需要在 微信支付商户平台 注册商户号。注册完成后,需要获取以下关键参数:
- 商户号(mchid):商户平台的唯一标识
- API 密钥(APIv3 Key):用于 API 签名的 32 位密钥
- 商户证书(pem 格式):商户私钥和证书序列号
- 微信支付平台证书:验证微信服务器响应的证书
1.2 配置支付回调域名
在商户平台 → 产品中心 → 开发配置中,设置 JSAPI 支付授权目录和 Native 支付回调链接。以源码商城为例,假设你的商城部署在 https://greenfield.ltd/store/,需要配置:
# JSAPI 支付授权目录 https://greenfield.ltd/store/order/ # Native 支付回调 URL https://greenfield.ltd/store/order/pay_notify.php # 退款回调 URL https://greenfield.ltd/store/order/refund_notify.php
二、Native 支付(扫码支付)实战
Native 支付是源码交易最常用的方式——用户打开商品页,选择微信支付,页面上展示二维码,扫码完成支付。下面给出完整的 PHP 实现。
2.1 统一下单接口
<?php // wechat_native_pay.php — Native 支付下单 function createNativeOrder($orderNo, $amount, $description) { $merchantId = 'YOUR_MCHID'; $serialNo = 'YOUR_CERT_SERIAL'; $apiKey = 'YOUR_API_V3_KEY'; $url = 'https://api.mch.weixin.qq.com/v3/pay/transactions/native'; $body = json_encode([ 'mchid' => $merchantId, 'out_trade_no' => $orderNo, 'appid' => 'YOUR_APPID', 'description' => $description, 'notify_url' => 'https://greenfield.ltd/store/order/pay_notify.php', 'amount' => ['total' => intval($amount * 100), 'currency' => 'CNY'] ]); // 生成签名(Wechatpay-Signature) $nonce = bin2hex(random_bytes(16)); $timestamp = time(); $message = "POST\n/v3/pay/transactions/native\n{$timestamp}\n{$nonce}\n{$body}\n"; $privateKey = openssl_get_privatekey(file_get_contents('/path/to/apiclient_key.pem')); openssl_sign($message, $signature, $privateKey, 'sha256WithRSAEncryption'); $sign = base64_encode($signature); $token = sprintf( 'mchid="%s",nonce_str="%s",timestamp="%d",serial_no="%s",signature="%s"', $merchantId, $nonce, $timestamp, $serialNo, $sign ); $ch = curl_init($url); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_POSTFIELDS => $body, CURLOPT_HTTPHEADER => [ 'Content-Type: application/json', 'Accept: application/json', "Wechatpay-Serial: {$serialNo}", "Authorization: WECHATPAY2-SHA256-RSA2048 {$token}" ], CURLOPT_RETURNTRANSFER => true, CURLOPT_SSL_VERIFYPEER => true, ]); $result = json_decode(curl_exec($ch), true); curl_close($ch); // 返回 code_url 即为支付二维码链接 return $result['code_url'] ?? null; } // 使用示例 $codeUrl = createNativeOrder('ORD20260630001', 99.00, '源码商城 - Codex Desktop授权'); // 将 $codeUrl 生成二维码展示给用户 ?>
返回的 code_url 是一段 URL,需要用二维码生成库(如 phpqrcode 或前端 qrcode.js)渲染为二维码图片展示给用户。
2.2 前端扫码展示
<div class="pay-qrcode">
<div id="qrcode"></div>
<p class="tip">请使用微信扫码支付</p>
</div>
<script src="https://cdn.jsdelivr.net/npm/qrcodejs@1.0.0/qrcode.min.js"></script>
<script>
new QRCode(document.getElementById('qrcode'), {
text: '= $codeUrl ?>',
width: 200,
height: 200
});
// 轮询支付状态
const pollTimer = setInterval(async () => {
const res = await fetch('/store/order/check.php?order=' + orderNo);
const data = await res.json();
if (data.status === 'success') {
clearInterval(pollTimer);
window.location.href = '/store/order/success.html?order=' + orderNo;
}
}, 2000);
</script>
三、支付回调与验签
支付回调是整套系统的"心脏"——只有正确处理好回调通知,才能实现自动发货。微信支付在用户完成支付后,会向你的 notify_url 发送 POST 请求,携带完整的支付结果信息。
3.1 回调处理示例
<?php // pay_notify.php — 微信支付回调处理 $incomingBody = file_get_contents('php://input'); $headers = getallheaders(); // 1. 获取微信签名字段 $wechatpaySignature = $headers['Wechatpay-Signature'] ?? ''; $wechatpayTimestamp = $headers['Wechatpay-Timestamp'] ?? ''; $wechatpayNonce = $headers['Wechatpay-Nonce'] ?? ''; $wechatpaySerial = $headers['Wechatpay-Serial'] ?? ''; // 2. 构造验签名串 $message = "{$wechatpayTimestamp}\n{$wechatpayNonce}\n{$incomingBody}\n"; // 3. 获取微信平台证书公钥(建议缓存) $platformPublicKey = getWechatPlatformCert($wechatpaySerial); // 4. 验证签名 $ok = openssl_verify( $message, base64_decode($wechatpaySignature), $platformPublicKey, 'sha256WithRSAEncryption' ); if (!$ok) { http_response_code(401); exit('FAIL'); } // 5. 解密资源(AES-256-GCM) $data = json_decode($incomingBody, true); $resource = $data['resource']; $ciphertext = base64_decode($resource['ciphertext']); $associatedData = $resource['associated_data']; $nonce = $resource['nonce']; $decrypted = openssl_decrypt( $ciphertext, 'aes-256-gcm', $apiKey, OPENSSL_RAW_DATA, $nonce, '', 16 ); $payResult = json_decode($decrypted, true); // 6. 处理订单 — 更新状态并触发发货 $orderNo = $payResult['out_trade_no']; $tradeNo = $payResult['transaction_id']; $payerOpenid = $payResult['payer']['openid']; $totalFee = $payResult['amount']['total']; // 单位:分 // 更新数据库订单状态 $db->query("UPDATE orders SET status='paid', trade_no='{$tradeNo}', paid_at=NOW() WHERE order_no='{$orderNo}' AND status='pending'"); // 触发自动发货(详见下一节) autoDeliverProduct($orderNo); // 7. 应答微信 — 必须返回 200 + SUCCESS http_response_code(200); echo '{"code":"SUCCESS","message":"成功"}'; ?>
💡 注意事项:回调处理幂等性非常重要——微信可能会重复发送多次回调通知,你的处理逻辑必须保证多次执行不会产生重复发货。建议用数据库唯一索引或 Redis 锁来保证幂等。
四、自动发货系统集成
源码交易和实物商品不同,用户付款后期望立即获得源码下载链接或授权码。自动发货系统就是支付成功后的"最后一公里"。
4.1 发货逻辑实现
<?php // auto_deliver.php — 自动发货 function autoDeliverProduct($orderNo) { // 获取订单信息 $order = $db->query("SELECT * FROM orders WHERE order_no='{$orderNo}'")->fetch(); switch ($order['product_type']) { case 'codex_desktop': // Codex Desktop: 生成授权码并发送 $license = generateLicenseKey($order['id']); $db->query("INSERT INTO licenses (order_id, license_key, status) VALUES ('{$order['id']}', '{$license}', 'active')"); // 发送通知(站内信 + 可选邮件) sendUserNotification($order['user_id'], "您的 Codex Desktop 授权码已生成:{$license}"); break; case 'card_qr_v3': // 转卡码系统:直接发货卡密 $card = assignCard($order['product_id'], $order['user_id']); sendUserNotification($order['user_id'], "您的卡密:{$card['card_no']},有效期至:{$card['expire_at']}"); break; case 'source_code': // 源码下载:生成临时下载链接(有限次/限时) $token = bin2hex(random_bytes(32)); $db->query("INSERT INTO download_tokens (order_id, token, expires_at) VALUES ('{$order['id']}', '{$token}', DATE_ADD(NOW(), INTERVAL 24 HOUR))"); $downloadUrl = "https://greenfield.ltd/store/download.php?token={$token}"; sendUserNotification($order['user_id'], "下载链接:{$downloadUrl}(24小时内有效)"); break; } } ?>
我们的 源码商城系统 内置了完善的自动发货模块,支持 Codex Desktop 授权码自动发放、转卡码系统卡密自动分配、源码下载链接生成等多种发货方式,无需从零开发。
五、JSAPI 支付(小程序/H5 内支付)
如果你的源码商城还有小程序端或微信公众号端,JSAPI 支付是必须对接的。JSAPI 支付流程比 Native 多一个步骤:需要先获取用户的 openid。
5.1 获取用户 OpenID
<?php // 通过 OAuth2 获取用户 openid $appid = 'YOUR_APPID'; $appsecret = 'YOUR_APPSECRET'; $code = $_GET['code']; // 微信回调传来的临时 code $url = "https://api.weixin.qq.com/sns/oauth2/access_token?" . "appid={$appid}&secret={$appsecret}&code={$code}&grant_type=authorization_code"; $resp = json_decode(file_get_contents($url), true); $openid = $resp['openid']; // 用户唯一标识 ?>
5.2 JSAPI 下单
<?php // JSAPI 下单与 Native 类似,但需传入 openid $body = json_encode([ 'mchid' => $merchantId, 'out_trade_no' => $orderNo, 'appid' => $appid, 'description' => $description, 'notify_url' => 'https://greenfield.ltd/store/order/pay_notify.php', 'amount' => ['total' => intval($amount * 100), 'currency' => 'CNY'], 'payer' => ['openid' => $openid] ]); // POST 到 https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi // 返回 prepay_id,传给前端调起支付 ?>
5.3 前端调起支付
<script> // 后端返回的 prepay 参数 const payParams = { appId: 'wx...', timeStamp: '...', nonceStr: '...', package: 'prepay_id=...', signType: 'RSA', paySign: '...' }; wx.chooseWXPay({ ...payParams, success: function(res) { // 支付成功,后端会处理回调,这里可以跳转到成功页 window.location.href = '/store/order/success.html?order=' + orderNo; }, fail: function(err) { alert('支付失败:' + err.errMsg); } }); </script>
六、支付对账与异常处理
6.1 每日对账脚本
生产环境每天建议跑一次对账,确保本地订单状态与微信商户平台一致:
#!/bin/bash # daily_reconcile.sh — 每日对账脚本 (cron 每天凌晨 2 点执行) DATE=$(date -d "yesterday" "+%Y-%m-%d") BALANCE_URL="https://api.mch.weixin.qq.com/v3/bill/tradebill?bill_date=${DATE}&bill_type=ALL" # 下载账单并比对本地数据库 php /var/www/html/store/scripts/reconcile.php --date=$DATE # 记录差异到日志 if [ $? -ne 0 ]; then echo "[ALERT] 对账异常!日期: $DATE" | mail -s "支付对账告警" admin@greenfield.ltd fi
6.2 常见问题排查
| 问题 | 可能原因 | 解决方案 |
|---|---|---|
| 回调收不到 | 防火墙/Nginx 未放行 IP 范围 | 放行微信回调 IP 段,检查 Nginx 日志 |
| 签名验证失败 | APIv3 Key 错误或证书过期 | 重新下载商户证书,检查平台证书序列号 |
| 自动发货未触发 | 回调未正确处理 | 检查回调日志,手动触发发货 |
| 二维码扫码提示"已过期" | code_url 有效期 2 小时 | 重新下单生成新二维码 |
| JSAPI "当前页面未注册" | 授权目录配置不正确 | 检查 JSAPI 支付授权目录是否精确匹配 |
七、安全最佳实践总结
在源码交易中接入微信支付,安全是重中之重。总结几条核心原则:
- 永远在服务端做签名:不要在客户端暴露 API 密钥和商户证书
- 回调通知必须验签:任何未通过签名的请求都拒绝处理
- 幂等处理不可少:重复回调不会导致重复发货
- 金额以分为单位:避免浮点数精度问题
- 订单号要有业务意义:建议格式
日期+用户ID+序号,便于排查 - 使用 APIv3 接口:v2 接口已逐步下线,新项目直接上 v3
- 定期更换 API 密钥:每 3-6 个月更换一次商户 API 密钥
微信支付接入看似简单,实际生产环境中要处理的各种边界情况非常多。如果你希望快速搭建一个带有完整微信支付对接、自动发货系统的源码商城,不妨直接使用我们的 源码商城产品——我们不仅提供了完整的支付模块源码,还包含了转卡码系统、Codex Desktop 自动分发等功能,帮你节省大量开发时间。
八、用 Python 实现的参考实现
如果你更习惯 Python,这里也提供一个使用 wechatpayv3 库的参考实现片段:
# pay.py — Python 版微信支付 Native 下单 from wechatpayv3 import WeChatPay, WeChatPayException wxpay = WeChatPay( appid = 'YOUR_APPID', mchid = 'YOUR_MCHID', api_key = 'YOUR_API_V3_KEY', private_key_path = '/path/to/apiclient_key.pem', cert_serial_no = 'YOUR_CERT_SERIAL', ) try: result = wxpay.native_pay( description = '源码商城 - 转卡码系统授权', out_trade_no = 'ORD20260630002', amount = 19900, # 单位:分 = ¥199 notify_url = 'https://greenfield.ltd/store/order/pay_notify.php', ) code_url = result['code_url'] print(f"支付二维码链接: {code_url}") except WeChatPayException as e: print(f"下单失败: {e}")
Python 版本非常适合在自动化脚本中使用,比如配合 Celery 进行异步支付状态检查。
结语
微信支付接入是源码商城的技术基石之一。从 Native 扫码到 JSAPI 内支付,从回调验签到自动发货,每个环节都需要精心设计和充分的异常处理。希望本文的实战代码能帮助你少走弯路,快速搭建一个稳定可靠的支付系统。
如果你对转卡码系统的支付对接、Codex Desktop 授权发放机制感兴趣,欢迎访问 源码商城 了解更多产品详情。我们提供完整的商城源码,内置微信支付、支付宝支付双通道,支持自动发货、代理分润等高级功能。