*接口文档
https://pay.weixin.qq.com/wiki/doc/apiv3/open/pay/chapter2_6_0.shtml
接入之前先 做好 后台开通 H5 支付,并且 后台 配置 相关域名信息,已经 证书。
证书 在 【API 安全】那里,可以查看 到 [商户API证书序列号] 以及 下载 商户私钥KEY 的 PEM 文件。
* 开始接入 的代码
https://pay.weixin.qq.com/wiki/doc/apiv3/open/pay/chapter2_6_2.shtml
去 https://github.com/wechatpay-apiv3/wechatpay-guzzle-middleware 下载 封装好的库 ,
* 其中 该库 需 使用 composer 进行 安装 , 没有 composer 参考:https://www.runoob.com/w3cnote/composer-install-and-usage.html 安装。
然后运行命令,加载 微信支付库
composer require wechatpay/wechatpay-guzzle-middleware
如果 加载不成功,则自己手动下载包,然后手动 include 相关文件。
* 另外 该库 还需用到 guzzlehttp 库。也可以使用 composer 安装
composer require guzzlehttp/guzzle
安装后 会 得到 vender 目录文件
如果 不能正常 加载 guzzle 库,则自己 手动拷贝这些文件 并 包含 autoload.php
* 其中 【商户API证书序列号】 在 商户后台 API 安全 那里可以看到, 【商户私钥文件】就是 商户后台下载那个。
【微信支付平台证书】就比较麻烦了。
参考 https://blog.csdn.net/ningxn/article/details/107344223
先是 获取 平台证书 及 序列号。获取的结果是 加密过的。
class GetCert{
/**
* 获取平台证书内容
*/
public function get_Certificates()
{
$merchant_id = '10000000001';//商户号,需改成自己的
$serial_no = 'KJASDFJLK23423424321';//API证书序列号,需改成自己的
$sign = $this->get_Sign("https://api.mch.weixin.qq.com/v3/certificates","GET","",$this->get_Privatekey(), $merchant_id, $serial_no);//$http_method要大写
$header[] = 'User-Agent:https://zh.wikipedia.org/wiki/User_agent';
$header[] = 'Accept:application/json';
$header[] = 'Authorization:WECHATPAY2-SHA256-RSA2048 ' . $sign;
$back=$this->http_Request("https://api.mch.weixin.qq.com/v3/certificates",$header);
print_r(json_decode($back,true));
}
/**
* 获取sign
* @param $url
* @param $http_method [POST GET 必读大写]
* @param $body [请求报文主体(必须进行json编码)]
* @param $mch_private_key [商户私钥]
* @param $merchant_id [商户号]
* @param $serial_no [证书编号]
* @return string
*/
private function get_Sign($url, $http_method, $body, $mch_private_key, $merchant_id, $serial_no)
{
$timestamp = time();//时间戳
$nonce = $timestamp . rand(10000, 99999);//随机字符串
$url_parts = parse_url($url);
$canonical_url = ($url_parts['path'] . (!empty($url_parts['query']) ? "?${url_parts['query']}" : ""));
$message =
$http_method . "\n" .
$canonical_url . "\n" .
$timestamp . "\n" .
$nonce . "\n" .
$body . "\n";
openssl_sign($message, $raw_sign, $mch_private_key, 'sha256WithRSAEncryption');
$sign = base64_encode($raw_sign);
$token = sprintf('mchid="%s",nonce_str="%s",timestamp="%d",serial_no="%s",signature="%s"',
$merchant_id, $nonce, $timestamp, $serial_no, $sign);
return $token;
}
/**
* 获取商户私钥
* @return false|resource
*/
public function get_Privatekey()
{
$private_key_file = './cert/apiclient_key.pem';//私钥文件路径,改成自己的。 如linux服务器秘钥地址地址:/www/wwwroot/test/key/private_key.pem"
$mch_private_key = openssl_get_privatekey(file_get_contents($private_key_file));//获取私钥
return $mch_private_key;
}
/**
* 数据请求
* @param $url
* @param array $header 获取头部
* @param string $post_data POST数据,不填写默认以GET方式请求
* @return bool|string
*/
public function http_Request($url, $header = array(), $post_data = "")
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 2);
if ($post_data != "") {
curl_setopt($ch, CURLOPT_POST, TRUE);
curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data); //设置post提交数据
}
//判断当前是不是有post数据的发
$output = curl_exec($ch);
if ($output === FALSE) {
$output = "curl 错误信息: " . curl_error($ch);
}
curl_close($ch);
return $output;
}
}
$a = new GetCert();
$a->get_Certificates();
获取 如下 类似的 结果
然后 要对这些 进行 解密。 解密 参考 官方 DEMO https://wechatpay-api.gitbook.io/wechatpay-api-v3/qian-ming-zhi-nan-1/zheng-shu-he-hui-tiao-bao-wen-jie-mi
因为 官方DEMO 的 PHP 解密 需要 PHP 7.1+ 的版本,所以 使用了 JAVA 版本的 解密。
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class AesUtil {
static final int KEY_LENGTH_BYTE = 32;
static final int TAG_LENGTH_BIT = 128;
private final byte[] aesKey;
public AesUtil(byte[] key) {
if (key.length != KEY_LENGTH_BYTE) {
throw new IllegalArgumentException("无效的ApiV3Key,长度必须为32个字节");
}
this.aesKey = key;
}
public String decryptToString(byte[] associatedData, byte[] nonce, String ciphertext)
throws GeneralSecurityException, IOException {
try {
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
SecretKeySpec key = new SecretKeySpec(aesKey, "AES");
GCMParameterSpec spec = new GCMParameterSpec(TAG_LENGTH_BIT, nonce);
cipher.init(Cipher.DECRYPT_MODE, key, spec);
cipher.updateAAD(associatedData);
return new String(cipher.doFinal(Base64.getDecoder().decode(ciphertext)), "utf-8");
} catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
throw new IllegalStateException(e);
} catch (InvalidKeyException | InvalidAlgorithmParameterException e) {
throw new IllegalArgumentException(e);
}
}
}
如果 电脑 只安装了 Android Studio 也可以 只允许 JAVA 调试。
参考 : https://www.jianshu.com/p/716a12a854c1
java 引用如下
import java.io.IOException;
import java.security.GeneralSecurityException;
public class MyClass {
public static void main(String[] args) {
byte[] key = "234sdfasdf234sdafsf".getBytes();
byte[] associatedData = "certificate".getBytes();
byte[] nonce = "2324dfsf".getBytes();
String ciphertext = "s5s8QCRHGQe+lca9YtES0QrFR15t2khc=======";
AesUtil as = new AesUtil(key);
try {
String certBack = as.decryptToString(associatedData,nonce,ciphertext);
System.out.print(certBack);
} catch (GeneralSecurityException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
然后 将 解密 结果 写入 TXT 文件 ,并重命名 .pem 用于 【微信支付平台证书】
** 如果提示 unable to get local issuer certificate 。 则下载个 ca-bundle.crt
https://github.com/bagder/ca-bundle/blob/e9175fec5d0c4d42de24ed6d84a06d504d5e5a09/ca-bundle.crt
放在服务器 ,并在 php.ini 中配置路径
curl.cainfo="d:/ca-bundle.crt"
至此, 该准备的 准备好了。开始如下代码:
withMerchant($merchantId, $merchantSerialNumber, $merchantPrivateKey) // 传入商户相关配置
->withWechatPay([ $wechatpayCertificate ]) // 可传入多个微信支付平台证书,参数类型为array
->build();
// 将WechatPayMiddleware添加到Guzzle的HandlerStack中
$stack = GuzzleHttp\HandlerStack::create();
$stack->push($wechatpayMiddleware, 'wechatpay');
// 创建Guzzle HTTP Client时,将HandlerStack传入,接下来,正常使用Guzzle发起API请求,WechatPayMiddleware会自动地处理签名和验签
$client = new GuzzleHttp\Client(['handler' => $stack]);
?>
request(
'POST',
'https://api.mch.weixin.qq.com/v3/pay/transactions/h5', //请求URL
[
// JSON请求体
'json' => [
"amount" => [
"total" => 100,
"currency" => "CNY",
],
"mchid" => "1002340241",
"description" => "Image形象店-深圳腾大-QQ公仔",
"notify_url" => "http://demo2.shijiehuashanglianmeng.com/demo/1/pay/1.php",
"out_trade_no" => "1217752501201407033233361001",
"goods_tag" => "WXG",
"appid" => "wx234ASDFASDF234",
"attach" => "自定义数据说明",
"scene_info" => [
"device_id" => "013467007045761",
"payer_client_ip" => $clt_ip,
"h5_info"=>[
"type"=>"Android"
],
]
],
'headers' => [ 'Accept' => 'application/json' ]
]
);
$statusCode = $resp->getStatusCode();
if ($statusCode == 200) { //处理成功
echo $reBody = $resp->getBody()->getContents();
$json = json_decode($reBody);
print_r($json);
echo '跳转';
} else if ($statusCode == 204) { //处理成功,无返回Body
echo "success";
}
} catch (RequestException $e) {
// 进行错误处理
echo '错误应答 '."
";
echo $e->getMessage()."\n";
if ($e->hasResponse()) {
echo "failed,resp code = " . $e->getResponse()->getStatusCode() . " return body = " . $e->getResponse()->getBody() . "\n";
}
return;
}
?>