开发指引
更新时间:2025.03.241. 接口规则
为了在保证支付安全的前提下,带给商户简单、一致且易用的开发体验,我们推出了全新的微信支付APIv3接口。该版本API的具体规则请参考“APIv3接口规则”
2. 开发环境搭建
JAVA
2.1.1. 使用条件
WechatPay merchant account
Java 1.8 or later
Maven or Gradle;
0.4.6 or later version of the Java httpClient SDK
2.1.2. 安装
Gradle:在你的 build.gradle 文件中加入如下的依赖
1implementation 'com.github.wechatpay-apiv3:wechatpay-apache-httpclient:0.4.7'
Maven:在 pom.xml 文件中加入以下依赖
1<dependency> 2<groupId>com.github.wechatpay-apiv3</groupId> 3<artifactId>wechatpay-apache-httpclient</artifactId> 4<version>0.4.7</version> 5</dependency>
2.1.3. 初始化
用WechatPayHttpClientBuilder初始化创建一个httpClient,httpClient将被用于发送请求调用微信支付接口。 用CertificatesManager初始化一个verifier实例,用于验证签名。
1 private CloseableHttpClient httpClient; 2 private CertificatesManager certificatesManager; 3 private Verifier verifier; 4 5 @Before 6 //initialize verifier and build httpClient 7 public void setup() throws GeneralSecurityException, IOException, HttpCodeException, NotFoundException { 8 9 PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(privateKey); 10 certificatesManager = CertificatesManager.getInstance(); 11 certificatesManager.putMerchant(merchantId, new WechatPay2Credentials(merchantId, 12 new PrivateKeySigner(merchantSerialNumber, merchantPrivateKey)), 13 apiV3Key.getBytes(StandardCharsets.UTF_8)); 14 verifier = certificatesManager.getVerifier(merchantId); 15 WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create() 16 .withMerchant(merchantId, merchantSerialNumber, merchantPrivateKey) 17 .withValidator(new WechatPay2Validator(verifier)); 18 httpClient = builder.build(); 19 }
2.1.4. 通用功能
敏感信息加密
1 @Test 2 public void encryptedTest() throws IllegalBlockSizeException { 3 String text = "helloWorld"; 4 String transformation = "RSA/ECB/PKCS1Padding"; 5 String encryptedText = RsaCryptoUtil.encrypt(text, verifier.getValidCertificate(), transformation); 6 System.out.println(encryptedText); 7 }
敏感信息解密
1 @Test 2 public void decryptedTest() throws BadPaddingException { 3 String encryptedText = OBTgun4jiN13n3nmSg8v0MNDMy3mbs380yq4GrgO8vwCqXnvrWxwo3jUCNY2UY7MIOtv/SD8ke64MxcSB0hn5EzKv1LOLprI3NvvmNLS4C3SBulxpZG62RYp1+8FgwAI4M//icXvjtZWVH4KVDg=="; 4 String transformation = "RSA/ECB/PKCS1Padding"; 5 String decryptedText = RsaCryptoUtil.decrypt(encryptedText, PemUtil.loadPrivateKey(privateKey), transformation); 6 assert("helloWorld".equals(decryptedText)); 7 }
Golang
2.2.1. 使用条件
WechatPay merchant account
GO SDK 0.2.13 or later version
2.2.2. 安装
1. 使用 Go Modules 管理你的项目
如果你的项目还不是使用 Go Modules 做依赖管理,在项目根目录下执行:
go mod init
2. 无需 clone 仓库中的代码,直接在项目目录中执行:
go get -u github.com/wechatpay-apiv3/wechatpay-go
来添加依赖,完成 go.mod 修改与 SDK 下载
2.2.3. 初始化
初始化Client并向Client中加载商户号,私钥,API V3 key,商户证书和序列号, 用于之后发送接口请求。
1import ( 2 "context" 3 "crypto/rand" 4 "crypto/rsa" 5 "crypto/x509" 6 "encoding/base64" 7 "fmt" 8 "github.com/wechatpay-apiv3/wechatpay-go/core" 9 "github.com/wechatpay-apiv3/wechatpay-go/core/option" 10 "github.com/wechatpay-apiv3/wechatpay-go/utils" 11 "log" 12 "net/http" 13) 14 15var ( 16 mchID = "" // merchant id 17 mchCertificateSerialNumber = "" // merchant certificate serial number 18 mchAPIv3Key = "" // merchant api v3 key 19 header = make(http.Header) 20 ctx = context.Background() 21 cert *x509.Certificate 22 mchPrivateKey *rsa.PrivateKey 23 client *core.Client 24 err error 25) 26 27func setup() { 28 //Load platform certificate 29 cert, err = utils.LoadCertificateWithPath("Path/To/Platform/Cert") 30 if err != nil { 31 log.Fatal(err) 32 } 33 // Load private key 34 mchPrivateKey, err = utils.LoadPrivateKeyWithPath("Path/To/Private/Key") 35 if err != nil { 36 log.Fatal("load merchant private key error") 37 } 38 // Initialize client which is capable to update platform certificate periodically 39 opts := []core.ClientOption{ 40 option.WithWechatPayAutoAuthCipher(mchID, mchCertificateSerialNumber, mchPrivateKey, mchAPIv3Key), 41 } 42 client, err = core.NewClient(ctx, opts...) 43 if err != nil { 44 log.Fatalf("new wechat pay client err:%s", err) 45 } 46}
2.2.4. 通用功能
敏感信息加密
1 2func Encryption(t *testing.T) { 3 var testRSACryptoUtilMchCertificateStr = `-----BEGIN CERTIFICATE----- 4 -----END CERTIFICATE-----` 5 testRSACryptoUtilCertificate, _ = LoadCertificate(testRSACryptoUtilMchCertificateStr) 6 const message = "hello world" 7 8 // 使用OAEP padding方式对证书加密 9 ciphertext, _ := EncryptOAEPWithCertificate(message, testRSACryptoUtilCertificate) 10 11 // 使用PKCS1 padding对证书加密 12 ciphertext, _ := EncryptPKCS1v15WithCertificate(message, testRSACryptoUtilCertificate) 13} 14
敏感信息解密
1func TestDecryption(t *testing.T) { 2 var testRSACryptoUtilPrivateKeyStr = `-----BEGIN PRIVATE KEY----- 3-----END PRIVATE KEY-----` 4 testRSACryptoUtilPrivateKey, _ = LoadPrivateKey(testingKey(testRSACryptoUtilPrivateKeyStr)) 5 const ciphertext = "" 6 7 // 使用PKCS1 padding进行私钥解密 8 decryptMessage, _ := DecryptPKCS1v15(ciphertext, testRSACryptoUtilPrivateKey) 9 10 // 使用OAEP padding方式私钥解密 11 decryptMessage, _ := DecryptOAEP(ciphertext, testRSACryptoUtilPrivateKey) 12}
PHP
2.3.1. 使用条件
Guzzle 7.0,PHP >= 7.2.5
Guzzle 6.5,PHP >= 7.1.2
项目已支持 PHP 8。
2.3.2. 安装
推荐使用 PHP 包管理工具 Composer 安装 SDK:
composer require wechatpay/wechatpay
2.3.3. 初始化
初始化Client并向Client中加载商户号,私钥,API V3 key,商户证书和序列号, 用于之后发送接口请求。
1 2require_once('vendor/autoload.php'); 3 4use WeChatPay\Builder; 5use WeChatPay\Crypto\Rsa; 6use WeChatPay\Util\PemUtil; 7 8$merchantId = '190000****'; 9$merchantPrivateKeyFilePath = 'file:///path/to/merchant/apiclient_key.pem'; 10$merchantPrivateKeyInstance = Rsa::from($merchantPrivateKeyFilePath, Rsa::KEY_TYPE_PRIVATE); 11$merchantCertificateSerial = '3775B6A45ACD588826D15E583A95F5DD********'; 12$platformCertificateFilePath = 'file:///path/to/wechatpay/cert.pem'; 13$platformPublicKeyInstance = Rsa::from($platformCertificateFilePath, Rsa::KEY_TYPE_PUBLIC); 14$platformCertificateSerial = PemUtil::parseCertificateSerialNo($platformCertificateFilePath); 15 16// init a API V3 instance 17$instance = Builder::factory([ 18 'mchid' => $merchantId, 19 'serial' => $merchantCertificateSerial, 20 'privateKey' => $merchantPrivateKeyInstance, 21 'certs' => [ 22 $platformCertificateSerial => $platformPublicKeyInstance, 23 ], 24]); 25
2.3.4. 通用功能
敏感信息加密
1 $encryptor = static function(string $msg) use ($platformPublicKeyInstance): string { 2 return Rsa::encrypt($msg, $platformPublicKeyInstance, OPENSSL_PKCS1_PADDING); 3 }; 4 $encryptedMsg = $encryptor('HelloWorld');
敏感信息解密
1 $decryptor = static function(string $msg) use ($merchantPrivateKeyInstance): string { 2 return Rsa::decrypt($msg, $merchantPrivateKeyInstance, OPENSSL_PKCS1_PADDING); 3 }; 4 $decryptedMsg = $decryptor('ciphertext');
3、快速接入
3.1、业务时序图
3.2、API调用示例
文档展示了如何使用微信支付服务端 SDK 快速接入JSAPI支付产品,完成与微信支付对接的部分。
注意:
文档中的代码示例是用来阐述 API 基本使用方法,代码中的示例参数需替换成商户自己账号及请求参数才能跑通。
以下接入步骤仅提供参考,请商户结合自身业务需求进行评估、修改。
3.2.1 小程序预签约
步骤说明:如 时序图2.2步骤 描述,当用户在商户小程序中选择同意使用代扣产品时,商户应请求小程序-预签约API 以获得预签约ID session_id,供下一步调起签约小程序使用。
代码示例 - JAVA:
1 2//Obtaining Signing Session ID API for mini-program signing 3 public void miniProgramSignTest() throws IOException { 4 String miniprogramSignBody = """{ 5"expired_time": "2021-11-20T13:29:35+08:00", 6"openid": "of8YZ6A_ySrPYzjX7joXo_0000", 7"out_contract_code": "20220614out_contract_code", 8"plan_id": 10001, 9"sp_appid": "wx7bc9000000000000", 10"sub_mchid": "90325355", 11"success_notify_url": "https://yoursite.com", 12"user_display_name": "mike" 13}"""; 14 HttpPost httpPost = new HttpPost("https://apihk.mch.weixin.qq.com/v3/global/papay/contracts/miniprogram-pre-entrust-sign"); 15 httpPost.addHeader("Accept", "application/json"); 16 httpPost.addHeader("Content-type", "application/json; charset=utf-8"); 17 httpPost.setEntity(new StringEntity(miniprogramSignBody)); 18 CloseableHttpResponse response = httpClient.execute(httpPost); 19 //Process the response 20 }
示例代码 - PHP:
1 2 //Mini Program Signing API 3 public function miniProgramSigning($instance) 4 { 5 try { 6 $resp = $instance 7 ->chain('v3/global/papay/contracts/miniprogram-pre-entrust-sign') 8 ->post([ 9 'json' => [ 10 "sp_appid" => "wx7bc9000000000000", 11 "sub_mchid" => "90325355", 12 "openid" => "of8YZ6A_ySrPYzjX7joXo_0000", 13 "out_contract_code" => "20220614out_contract_code", 14 "plan_id" => 10001, 15 "success_notify_url" => "https://www.yoursite.com", 16 "user_display_name" => "mike", 17 "expired_time" => "2021-11-20T13:29:35+08:00", 18 ] 19 ]); 20 21 echo $resp->getStatusCode(), PHP_EOL; 22 echo $resp->getBody(), PHP_EOL; 23 } catch (\Exception $e) { 24 // Exception handling 25 } 26 }
示例代码 - GO:
1 2//Mini program signing API 3func miniProgramSigning() { 4 signBody := `{ 5"expired_time": "2021-11-20T13:29:35+08:00", 6"openid": "of8YZ6A_ySrPYzjX7joXo_0000", 7"out_contract_code": "20220614out_contract_code", 8"plan_id": 10001, 9"sp_appid": "wx7bc9000000000000", 10"sub_mchid": "90325355", 11"success_notify_url": "https://yoursite.com", 12"user_display_name": "mike" 13}` 14 15 result, err := client.Post(ctx, "https://apihk.mch.weixin.qq.com/v3/global/papay/contracts/miniprogram-pre-entrust-sign", signBody) 16 if err != nil { 17 // Process error 18 } 19 log.Printf("%s", result.Response.Body)
3.2.2 小程序调起签约
步骤说明:
解析 3.1小程序预签约 接口返回的 Body json字符串,获取其中的 session_id,返回示例: { "session_id": "201710180325670965" }
使用上一步得到的 session_id 参数拉起签约小程序
示例代码:
1 2 var session_id = "201710180325670965"; // obtained from miniprogram-pre-entrust-sign 3// start signing process 4wx.navigateToMiniProgram({ 5 appId: wxbd687630cd02ce1d, 6 path: 'pages/Oversea/walletSelect?sessionId=' + session_id, 7 extraData: {}, 8 success(res) { 9 // Jumped to the signing mini program successfully 10 }, 11 fail(res) { 12 // Failed to jump to the signing mini program 13 } 14}) 15 16 17// After signing process, user will return back to merchant's miniprogram 18App({ 19 onShow(res) { 20 if (res.scene === 1038) { //Scenario value 1038: return from the opened min program 21 const { appId, extraData } = res.referrerInfo 22 if (appId == 'wxbd687630cd02ce1d') { // appId is wxbd687630cd02ce1d: jump back from the signing min program 23 if (typeof extraData == 'undefined') { 24 // TODO 25 // The client min program is not sure of the signing result and needs to request the merchant-side background to confirm the signing result 26 return; 27 } 28 if (extraData.return_code == 'SUCCESS') { 29 // TODO 30 // The client min program signs successfully and needs to request the merchant-side background to confirm the signing result 31 var contract_id = extraData.contract_id 32 return; 33 } 34 else { 35 // TODO 36 // Signing failed 37 return; 38 } 39 } 40 } 41 } 42})
3.2.3 签约结果通知
步骤说明:用户完成签约后,微信支付会向发起签约时传入的success_notify_url推送签约结果通知,商户需要在接收到通知后返回对应的信息。
签约通知示例
1 2 // 签约成功回调 http body 数据示例 3{ 4 "id":"EV-2018022511223320873", 5 "create_time":"2022-06-14T14:01:35+08:00", 6 "resource_type":"encrypt-resource", 7 "event_type":"PAPAY.SIGN", 8 "summary": "签约成功", 9 "resource" : { 10 "algorithm":"AEAD_AES_256_GCM", 11 "ciphertext": "...", 12 "nonce": "dAvnRJWFOfdL", 13 "associated_data": "papay" 14 } 15} 16// 对 resource.ciphertext 解密后得到的数据示例如下: 17{ 18 "sp_mchid":"10000091", 19 "sub_mchid":"10000097", 20 "out_contract_code":"100001256", 21 "plan_id":123, 22 "contract_id":"Wx15463511252015071056489715", 23 "sp_appid":"wxcbda96de0b165486", 24 "openid":"ouFhd5X9s9WteC3eWRjXV3lea123", 25 "operate_time":"2015-09-01T10:00:00+08:00" 26} 27 28// 解约成功回调 http body 数据示例 29{ 30 "id":"2646bee0-aef8-5804-9752-ef1a56170fdf", 31 "create_time":"2022-06-14T16:31:53+08:00", 32 "resource_type":"encrypt-resource", 33 "event_type":"PAPAY.TERMINATE", 34 "summary":"解约成功", 35 "resource":{ 36 "original_type":"papay", 37 "algorithm":"AEAD_AES_256_GCM", 38 "ciphertext": "...", 39 "associated_data":"papay", 40 "nonce":"SExJ2Xxx7sr1" 41 } 42} 43// 对 resource.ciphertext 解密后得到的数据示例如下: 44{ 45 "sp_mchid": "10000091", 46 "sub_mchid": "10000097", 47 "out_contract_code": "100001256", 48 "plan_id": 123, 49 "contract_id": "Wx15463511252015071056489715", 50 "sp_appid": "wxcbda96de0b165486", 51 "openid": "ouFhd5X9s9WteC3eWRjXV3lea123", 52 "contract_termination_mode": "USER", 53 "operate_time": "2015-10-01T10:00:00+08:00" 54}
当商户接收到微信支付回调通知时,应更新本地存储的用户签约状态,更新完成后返回如下 http body给微信支付,http状态应设置为200。
1{ 2 "code": "SUCCESS", 3 "message": "OK" 4}
注意:
支付结果通知是以POST 方法访问商户设置的通知url,通知的数据以JSON 格式通过请求主体(BODY)传输。通知的数据包括了加密的支付结果详情。
加密不能保证通知请求来自微信。微信会对发送给商户的通知进行签名,并将签名值放在通知的HTTP头Wechatpay-Signature。商户应当验证签名,以确认请求来自微信,而不是其他的第三方。签名验证的算法请参考《微信支付API V3签名验证》 。
支付通知http应答码为200或204才会当作正常接收,当回调处理异常时,应答的HTTP状态码应为500,或者4xx。
商户成功接收到回调通知后应返回成功的http应答码为200或204。
同样的通知可能会多次发送给商户系统。商户系统必须能够正确处理重复的通知。 推荐的做法是,当商户系统收到通知进行处理时,先检查对应业务数据的状态,并判断该通知是否已经处理。如果未处理,则再进行处理;如果已处理,则直接返回结果成功。在对业务数据进行状态检查和处理之前,要采用数据锁进行并发控制,以避免函数重入造成的数据混乱。
对后台通知交互时,如果微信收到商户的应答不符合规范或超时,微信认为通知失败,微信会通过一定的策略定期重新发起通知,尽可能提高通知的成功率,但微信不保证通知最终能成功。(通知频率为15s/15s/30s/3m/10m/20m/30m/30m/30m/60m/3h/3h/3h/6h/6h - 总计 24h4m)
3.2.4 查询签约结果
步骤说明:当商户后台、网络、服务器等出现异常,商户系统最终未接收到签约结果通知时商户可通过查询签约结果API核实签约状态。微信支付提供了两种查询签约状态的方式,一种是通过商户签约号即out_contract_code,另一种是通过微信支付返回的微信签约号contract_id。
JAVA - 示例代码:
通过商户签约号out_contract_code查询签约状态
1 2//Querying Signing Status (By out_contract_code) 3 public void querySignStatusByContractCodeTest() throws IOException { 4 HttpGet httpGet = new HttpGet("https://apihk.mch.weixin.qq.com/v3/global/papay/contracts/out-contract-code/100001261?sp_appid=wxcbda96de0b165486&sub_appid=wxcbda96de0b165484&plan_id=123"); 5 httpGet.addHeader("Accept", "application/json"); 6 httpGet.addHeader("Content-type", "application/json; charset=utf-8"); 7 CloseableHttpResponse response = httpClient.execute(httpGet); 8 //Process the response 9 }
通过微信签约号contract_id查询签约状态
1 2//Querying Signing Status (By contract_id) API 3 public void querySignStatusTest() throws IOException { 4 HttpGet httpGet = new HttpGet("https://apihk.mch.weixin.qq.com/v3/global/papay/contracts/202203242337333903387184301572?sub_mchid=10000097&sp_appid=wxcbda96de0b165486&sub_appid=wxcbda96de0b165484"); 5 httpGet.addHeader("Accept", "application/json"); 6 httpGet.addHeader("Content-type", "application/json; charset=utf-8"); 7 CloseableHttpResponse response = httpClient.execute(httpGet); 8 //Process the response 9 }
PHP - 示例代码:
通过商户签约号out_contract_code查询签约状态
1 2 //Querying Signing Status (By out_contract_code) API 3 public function querySignStatusByContractCode($instance) 4 { 5 try { 6 $resp = $instance 7 ->v3->global->papay->contracts->outContractCode->_out_contract_code_ 8 ->get([ 9 'query' => [ 10 'sp_appid' => 'wxcbda96de0000006', 11 'sub_appid' => 'wxcbda96de0000004', 12 'sub_mchid' => '110000000' 13 ], 14 '_out_contract_code_' => '100005698' 15 ]); 16 17 echo $resp->getStatusCode(), PHP_EOL; 18 echo $resp->getBody(), PHP_EOL; 19 } catch (\Exception $e) { 20 // Exception handling 21 } 22 }
通过微信签约号contract_id查询签约状态
1 2 //Querying Signing Status (By contract_id) API 3 public function querySignStatus($instance) 4 { 5 try { 6 $resp = $instance 7 ->v3->global->papay->contracts->_contract_id_ 8 ->get([ 9 'query' => [ 10 'sp_appid' => 'wxcbda96de0000006', 11 'sub_appid' => 'wxcbda96de0000004', 12 'sub_mchid' => '110000000' 13 ], 14 'contract_id' => '202203242337333903387184301572' 15 ]); 16 17 echo $resp->getStatusCode(), PHP_EOL; 18 echo $resp->getBody(), PHP_EOL; 19 } catch (\Exception $e) { 20 // Exception handling 21 } 22 }
GO - 示例代码:
通过商户签约号out_contract_code查询签约状态
1 2//Querying signing status by out_contract_code API 3func querySigningStatusByContractCode() { 4 result, err := client.Get(ctx, "https://apihk.mch.weixin.qq.com/v3/global/papay/contracts/out-contract-code/100001261?sp_appid=wxcbda96de0b165486&sub_appid=wxcbda96de0b165484&plan_id=123") 5 if err != nil { 6 // Process error 7 } 8 log.Printf("status=%d resp=%s", result.Response.StatusCode, result.Response.Body) 9}
通过微信签约号contract_id查询签约状态
1 2//Querying signing status API 3func querySigningStatus() { 4 result, err := client.Get(ctx, "https://apihk.mch.weixin.qq.com/v3/global/papay/contracts/202203242337333903387184301572?sub_mchid=10000097&sp_appid=wxcbda96de0b165486&sub_appid=wxcbda96de0b165484") 5 if err != nil { 6 // Process error 7 } 8 log.Printf("status=%d resp=%s", result.Response.StatusCode, result.Response.Body) 9}
重要参数:
contract_id - 微信测返回的签约协议号,唯一标识一条签约协议,示例值:202203242337333903387184301572
out_contract_code - 商户侧生成的签约协议号,示例值:100001261
sp_appid - 服务商公众号的appid
sub_appid - 发起签约的子商户小程序appid
3.2.5 扣款
步骤说明:在完成签约后,商户可使用对应的协议ID发起扣款
代码示例 - JAVA:
1 2//Applying for Termination API 3public void deductionTest() throws IOException { 4 String deductionBody = """ 5{ 6 "sp_appid": "wxcbda96de0b165486", 7 "sub_mchid": "10000097", 8 "sub_appid": "wxcbda96de0b165484", 9 "description": "PAPAuto-debit支付测试", 10 "attach": "支付测试", 11 "notify_url": "https://wxpay.wxutil.com/pub_v2/pay/notify.v2.php", 12 "out_trade_no": "1217752501201407033233368018", 13 "goods_tag": "WXG", 14 "merchant_category_code": "1011", 15 "contract_id": "Wx15463511252015071056489715", 16 "amount": { 17 "total": 10000, 18 "currency": "HKD" 19 }, 20 "scene_info": { 21 "device_ip": "59.37.125.32", 22 "device_id": "013467007045764" 23 } 24} 25"""; 26 HttpPost httpPost = new HttpPost("https://apihk.mch.weixin.qq.com/v3/global/papay/transactions"); 27 httpPost.addHeader("Accept", "application/json"); 28 httpPost.addHeader("Content-type", "application/json; charset=utf-8"); 29 httpPost.setEntity(new StringEntity(deductionBody)); 30 CloseableHttpResponse response = httpClient.execute(httpPost); 31 //Process the response 32}
示例代码 - PHP:
1 2 //Deduction API 3 public function deduction($instance) 4 { 5 try { 6 $resp = $instance 7 ->v3->global->papay->transactions 8 ->post([ 9 'json' => [ 10 "sp_appid" => "wxcbda96de0b165486", 11 "sub_mchid" => "10000097", 12 "sub_appid" => "wxcbda96de0b165484", 13 "description" => "PAPAuto-debit支付测试", 14 "attach" => "支付测试", 15 "notify_url" => "https://wxpay.wxutil.com/pub_v2/pay/notify.v2.php", 16 "out_trade_no" => "1217752501201407033233368018", 17 "goods_tag" => "WXG", 18 "merchant_category_code" => "1011", 19 "contract_id" => "Wx15463511252015071056489715", 20 "amount" => array( 21 "total" => 10000, 22 "currency" => "HKD" 23 ), 24 "scene_info" => array( 25 "device_ip" => "59.37.125.32", 26 "device_id" => "013467007045764" 27 ) 28 ] 29 ]); 30 echo $resp->getStatusCode(), PHP_EOL; 31 echo $resp->getBody(), PHP_EOL; 32 } catch (\Exception $e) { 33 // Exception handling 34 } 35 }
示例代码 - GO:
1 2//Deduction API 3func deduction() { 4 body := ` 5{ 6 "sp_appid": "wxcbda96de0b165486", 7 "sub_mchid": "10000097", 8 "sub_appid": "wxcbda96de0b165484", 9 "description": "PAPAuto-debit支付测试", 10 "attach": "支付测试", 11 "notify_url": "https://wxpay.wxutil.com/pub_v2/pay/notify.v2.php", 12 "out_trade_no": "1217752501201407033233368018", 13 "goods_tag": "WXG", 14 "merchant_category_code": "1011", 15 "contract_id": "Wx15463511252015071056489715", 16 "amount": { 17 "total": 10000, 18 "currency": "HKD" 19 }, 20 "scene_info": { 21 "device_ip": "59.37.125.32", 22 "device_id": "013467007045764" 23 } 24} 25` 26 result, err := client.Post(ctx, "https://apihk.mch.weixin.qq.com/v3/global/papay/transactions", body) 27 if err != nil { 28 // Process error 29 } 30 log.Printf("%s", result.Response.Body) 31}
重要参数:
contract_id:用户签约协议号,微信支付根据该字段校验用户和商户的签约关系,如果未签约或者用户已解约则返回 NO_AUTH - 签约协议不存在
3.2.6 扣款结果通知
步骤说明:扣款成功后,微信支付会以异步通知的方式通过扣款请求中的 notify_url 参数告知商户扣款已成功。
1 2// 扣款结果通知 http body 数据示例 3{ 4 "id":"EV-2018022511223320873", 5 "create_time":"20180225112233", 6 "resource_type":"encrypt-resource", 7 "event_type":"TRANSACTION.SUCCESS", 8 "resource" : { 9 "algorithm":"AEAD_AES_256_GCM", 10 "ciphertext": "...", 11 "nonce": "...", 12 "associated_data": "" 13 } 14} 15// 对 resource.ciphertext 解密后得到的数据示例如下: 16{ 17 "sp_mchid": "10000100", 18 "sp_appid": "wx2421b1c4370ec43b", 19 "sub_mchid": "20000100", 20 "out_trade_no": "20150806125346", 21 "transaction_id": "1008450740201411110005820873", 22 "attach": "支付测试", 23 "trade_type": "AUTH", 24 "bank_type": "CCB_DEBIT", 25 "success_time": "2018-06-08T10:34:56+08:00", 26 "trade_state": "SUCCESS", 27 "trade_state_desc": "支付成功", 28 "merchant_category_code": "1011", 29 "contract_id": "Wx15463511252015071056489715", 30 "payer": { 31 "sp_openid": "oUpF8uN95-Ptaags6E_roPHg7AG0" 32 }, 33 "amount": { 34 "total": 528800, 35 "currency": "HKD", 36 "payer_total": 518799, 37 "payer_currency": "CNY", 38 "exchange_rate": { 39 "type": "SETTLEMENT_RATE", 40 "rate": 8000000 41 } 42 }, 43 "promotion_detail": [{ 44 "promotion_id": "109519", 45 "name": "单品惠-6", 46 "scope": "SINGLE", 47 "type": "DISCOUNT", 48 "amount": 1, 49 "currency": "HKD", 50 "activity_id": "931386", 51 "wechatpay_contribute_amount": 1, 52 "merchant_contribute_amount": 0, 53 "other_contribute_amount": 0, 54 "goods_detail": [{ 55 "goods_id": "iphone6s_16G", 56 "goods_remark": "商品备注", 57 "quantity": 1, 58 "price": 528800 59 }] 60 }] 61}
3.2.7 订单查询API
步骤说明:当商户后台、网络、服务器等出现异常,商户系统最终未接收到支付通知时商户可通过查询订单接口核实订单支付状态。
代码示例 - JAVA:
1 2 //Query transaction details by transaction_id 3 public void queryOrderByIdTest() throws IOException { 4 HttpGet httpGet = new HttpGet("https://apihk.mch.weixin.qq.com/v3/global/papay/transactions/4015463511252015071056489715?sub_mchid=10000097"); 5 httpGet.addHeader("Accept", "application/json"); 6 httpGet.addHeader("Content-type", "application/json; charset=utf-8"); 7 CloseableHttpResponse response = httpClient.execute(httpGet); 8 //Process the response 9 } 10 11 //Query transaction details by out_trade_no 12 public void queryOrderByNoTest() throws IOException { 13 HttpGet httpGet = new HttpGet("https://apihk.mch.weixin.qq.com/v3/global/papay/transactions/out_trade_no/1217752501201407033233368018?sub_mchid=10000097"); 14 httpGet.addHeader("Accept", "application/json"); 15 httpGet.addHeader("Content-type", "application/json; charset=utf-8"); 16 CloseableHttpResponse response = httpClient.execute(httpGet); 17 //Process the response 18 }
示例代码 - PHP:
1 2// Query transaction details by transaction_id 3public function queryOrderById($instance) 4{ 5 try { 6 $resp = $instance 7 ->v3->global->papay->transactions->_transaction_id_ 8 ->get([ 9 'query' => [ 10 'sub_mchid' => '10000097', 11 ], 12 'transaction_id' => '4015463511252015071056489715' 13 ] 14 ); 15 16 echo $resp->getStatusCode(), PHP_EOL; 17 echo $resp->getBody(), PHP_EOL; 18 } catch (Exception $e) { 19 // Exception handling 20 } 21} 22 23// Query transaction details by out_trade_no 24public function queryOrderByNo($instance) 25{ 26 try { 27 $resp = $instance 28 ->v3->global->papay->transactions->outTradeNo->_out_trade_no_ 29 ->get([ 30 'query' => [ 31 'sub_mchid' => '10000097', 32 ], 33 'out_trade_no' => '1217752501201407033233368018' 34 ] 35 ); 36 37 echo $resp->getStatusCode(), PHP_EOL; 38 echo $resp->getBody(), PHP_EOL; 39 } catch (Exception $e) { 40 // Exception handling 41 } 42}
示例代码 - GO:
1 2//Query transaction details by transaction_id 3func queryOrderById() { 4 result, err := client.Get(ctx, "https://apihk.mch.weixin.qq.com/v3/global/papay/transactions/4015463511252015071056489715?sub_mchid=10000097") 5 if err != nil { 6 // Process error 7 } 8 log.Printf("%s", result.Response.Body) 9} 10 11 12//Query transaction details by out_trade_no 13func queryOrderByNo() { 14 result, err := client.Get(ctx, "https://apihk.mch.weixin.qq.com/v3/global/papay/transactions/out_trade_no/1217752501201407033233368018?sub_mchid=10000097") 15 if err != nil { 16 // Process error 17 } 18 log.Printf("%s", result.Response.Body) 19}
重要参数:
out_trade_no:商户侧扣费订单号
transaction_id:微信订单号
3.2.8 【服务端】关闭订单
步骤说明:当商户订单支付失败需要生成新单号重新发起支付,要对原订单号调用关单,避免重复支付;系统下单后,用户支付超时,系统退出不再受理,避免用户继续,请调用关单接口
代码示例 - JAVA:
1 2//Applying for close order 3public void closeOrderTest() throws IOException { 4 String closeOrderBody = """ 5{ 6 "sub_mchid": "20000100" 7} 8"""; 9 HttpPost httpPost = new HttpPost("https://apihk.mch.weixin.qq.com/v3/global/papay/transactions/out-trade-no/1217752501201407033233368018/reverse"); 10 httpPost.addHeader("Accept", "application/json"); 11 httpPost.addHeader("Content-type", "application/json; charset=utf-8"); 12 httpPost.setEntity(new StringEntity(closeOrderBody )); 13 CloseableHttpResponse response = httpClient.execute(httpPost); 14 //Process the response 15}
示例代码 - PHP:
1 2 // Call close order API 3 public function closeOrder($instance) 4 { 5 try { 6 $resp = $instance 7 ->v3->global->papay->transactions->outTradeNo->_out_trade_no_->reverse 8 ->post([ 9 'json' => [ 10 'sub_mchid' => '20000100' 11 ], 12 'out_trade_no' => '1217752501201407033233368018' 13 ]); 14 echo $resp->getStatusCode(), PHP_EOL; 15 echo $resp->getBody(), PHP_EOL; 16 } catch (Exception $e) { 17 // Exception handling 18 } 19 }
示例代码 - GO:
1 2//Deduction API 3func closeOrder() { 4 body := ` 5{ 6 "sub_mchid": "20000100" 7} 8` 9 result, err := client.Post(ctx, "https://apihk.mch.weixin.qq.com/v3/global/papay/transactions/out-trade-no/1217752501201407033233368018/reverse", body) 10 if err != nil { 11 // Process error 12 } 13 log.Printf("%s", result.Response.Body) 14}
重要参数:
out_trade_no:商户侧扣费订单号
3.2.9 【服务端】解约
步骤说明:当用户希望在商户侧发起解约时,商户需调用解约API来完成解约。
代码示例 - JAVA:
1 2//Applying for Termination API 3public void deductionTest() throws IOException { 4 String terminateBody = """ 5{ 6"sp_appid": "wxcbda96de0b165486", 7"sub_mchid": "10000097", 8"sub_appid": "wxcbda96de0b165484", 9"termination_note": "Cause of termination" 10} 11"""; 12 HttpPost httpPost = new HttpPost("https://apihk.mch.weixin.qq.com/v3/global/papay/contracts/100005698/terminate"); 13 httpPost.addHeader("Accept", "application/json"); 14 httpPost.addHeader("Content-type", "application/json; charset=utf-8"); 15 httpPost.setEntity(new StringEntity(terminateBody)); 16 CloseableHttpResponse response = httpClient.execute(httpPost); 17 //Process the response 18 }
示例代码 - PHP:
1 2 //Applying for Termination API 3 public function termination($instance) 4 { 5 try { 6 $resp = $instance 7 ->v3->global->papay->contracts->_contract_id_->terminate 8 ->post([ 9 'json' => [ 10 'sp_appid' => 'wxcbda96de0b165486', 11 'sub_mchid' => '10000097', 12 'sub_appid' => 'wxcbda96de0b165484', 13 'termination_note' => 'Cause of termination' 14 ], 15 '_contract_id_' => '100005698' 16 ]); 17 echo $resp->getStatusCode(), PHP_EOL; 18 echo $resp->getBody(), PHP_EOL; 19 } catch (\Exception $e) { 20 // Exception handling 21 } 22 }
示例代码 - GO:
1 2//Applying for Termination API 3func terminate() { 4 body := ` 5{ 6"sp_appid": "wxcbda96de0b165486", 7"sub_mchid": "10000097", 8"sub_appid": "wxcbda96de0b165484", 9"termination_note": "Cause of termination" 10} 11` 12 result, err := client.Post(ctx, "https://apihk.mch.weixin.qq.com/v3/global/papay/contracts/100005698/terminate", body) 13 if err != nil { 14 // Process error 15 } 16 log.Printf("%s", result.Response.Body) 17}
重要参数:
contract_id:微信侧代扣协议ID