微信支付 H5 版本 PHP

*接口文档

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"

 


至此, 该准备的 准备好了。开始如下代码:

<?PHP

require './vendor/autoload.php';
  
use GuzzleHttp\Exception\RequestException;
use WechatPay\GuzzleMiddleware\WechatPayMiddleware;
use WechatPay\GuzzleMiddleware\Util\PemUtil;
use GuzzleHttp\HandlerStack;

 

include("./cert/Credentials.php");
include("./cert/Signer.php");

include("./cert/Verifier.php");
include("./cert/CertificateVerifier.php");
include("./cert/Validator.php");

include("./cert/WechatPay2Validator.php");
include("./cert/PrivateKeySigner.php");

include("./cert/PemUtil.php");
include("./cert/WechatPayMiddleware.php");
include("./cert/WechatPayMiddlewareBuilder.php");
include("./cert/WechatPay2Credentials.php");

//
function GetIP(){ 
	if (getenv("HTTP_CLIENT_IP") && strcasecmp(getenv("HTTP_CLIENT_IP"), "unknown")) 
	$ip = getenv("HTTP_CLIENT_IP"); 
	else if (getenv("HTTP_X_FORWARDED_FOR") && strcasecmp(getenv("HTTP_X_FORWARDED_FOR"), "unknown")) 
	$ip = getenv("HTTP_X_FORWARDED_FOR"); 
	else if (getenv("REMOTE_ADDR") && strcasecmp(getenv("REMOTE_ADDR"), "unknown")) 
	$ip = getenv("REMOTE_ADDR"); 
	else if (isset($_SERVER['REMOTE_ADDR']) && $_SERVER['REMOTE_ADDR'] && strcasecmp($_SERVER['REMOTE_ADDR'], "unknown")) 
	$ip = $_SERVER['REMOTE_ADDR']; 
	else 
	$ip = "unknown"; 
	return($ip); 
}
 
// 商户相关配置,
$merchantId = '16100000001'; // 商户号
$merchantSerialNumber = '34KL32J4LKJ2L41234234'; // 商户API证书序列号
$merchantPrivateKey = PemUtil::loadPrivateKey('./cert/apiclient_key.pem'); // 商户私钥文件路径
 
// 微信支付平台配置
$wechatpayCertificate = PemUtil::loadCertificate('./cert/wx_cert.pem'); // 微信支付平台证书文件路径
 
// 构造一个WechatPayMiddleware
$wechatpayMiddleware = WechatPayMiddleware::builder()
    ->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]);
 ?> 

 <?PHP
 
 $clt_ip = GetIP();

try {
	$resp = $client->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 '<a href="'.$json->h5_url.'" >跳转</a>';
 
	} else if ($statusCode == 204) { //处理成功,无返回Body
		echo "success";
	}

} catch (RequestException $e) {
	// 进行错误处理
	echo '错误应答 '."<BR><BR>";
	echo $e->getMessage()."\n";
	if ($e->hasResponse()) {
		echo "failed,resp code = " . $e->getResponse()->getStatusCode() . " return body = " . $e->getResponse()->getBody() . "\n";
	}
	return;
}


?>

 

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值