微信官方文档:https://pay.weixin.qq.com/wiki/doc/api/tools/mch_pay.php?chapter=24_2
1.第一步准备好商户的证书,在商户后台下载
2第二步准备好微信RSA公钥
微信官方地址:https://pay.weixin.qq.com/wiki/doc/api/tools/mch_pay.php?chapter=24_7
这里简单实例展示,需要的自行拆分封装
$mch_id = 123456;//自己的商户号
$str = md5(rand(1000,9999));//随机字符串
$sign_type = "MD5";//加密类型
$key = "abcde";//商户的key
$stringA = "mch_id={$mch_id}&nonce_str={$str}&sign_type={$sign_type}&key={$key}";
$md5_str = MD5($stringA);//md5加密
$sign = strtoupper($md5_str);//转大写
$url = "https://fraud.mch.weixin.qq.com/risk/getpublickey";//微信生成RSA公钥接口地址
$xml=<<<EOF
<xml>
<mch_id>{$mch_id}</mch_id>
<nonce_str>{$str}</nonce_str>
<sign_type>MD5</sign_type>
<sign>$sign</sign>
</xml>
EOF;
$apiclient_key = './Xcxpay/Certificate/Jw/apiclient_cert.pem';//证书路径 在商户平台下载
$apiclient_cert = './Xcxpay/Certificate/Jw/apiclient_key.pem';//证书路径 在商户平台下载
$ch = curl_init();
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
curl_setopt($ch,CURLOPT_URL, $url);
curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,TRUE);
curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,2);//严格校验
curl_setopt($ch, CURLOPT_HEADER, FALSE);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
//使用证书:cert 与 key 分别属于两个.pem文件
curl_setopt($ch,CURLOPT_SSLCERTTYPE,'PEM');
// curl_setopt($ch,CURLOPT_SSLCERT, 'apiclient_key证书的路径');//上面第一步在商户后台下载的证书
curl_setopt($ch,CURLOPT_SSLCERT, $apiclient_key);//上面第一步在商户后台下载的证书
curl_setopt($ch,CURLOPT_SSLKEYTYPE,'PEM');
// curl_setopt($ch,CURLOPT_SSLKEY, 'apiclient_cert证书的路径');//上面第一步在商户后台下载的证书
curl_setopt($ch,CURLOPT_SSLKEY, $apiclient_cert);//上面第一步在商户后台下载的证书
curl_setopt($ch, CURLOPT_POST, TRUE);
curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
$data = curl_exec($ch);
//返回结果
if($data){
curl_close($ch);
} else {
$error = curl_errno($ch);
curl_close($ch);
}
libxml_disable_entity_loader(true);
$result = json_decode(json_encode(simplexml_load_string($data, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
print_r($result);
成功后返回的数据
把pub_key的内容保存成为一个文件, 已 .pem后缀的文件 例如 wx_public.pem 等等需要用到这个文件
如:
再将wx_public.pem 格式转换
openssl rsa -RSAPublicKey_in -in 刚刚保存的文件 -pubout > 新的文件
得到新的pem文件,等等要用到。这个文件主要是要来RAS加密 银行卡号和姓名
新建一个类
class Wxyxk
{
public $value=array(
'mch_id'=>'',
'partner_trade_no'=>'',
'nonce_str'=>'',
'enc_bank_no'=>'',
'enc_true_name'=>'',
'bank_code'=>'',
'amount'=>'',
'desc'=>'',
);
public $app_id;
public $mch_id;
public $key;
public $url = "https://api.mch.weixin.qq.com/mmpaysptrans/pay_bank";
public $apiclient_cert;
public $apiclient_key;
public $public_pem;
public function __construct($mch_id, $key)
{
$this->mch_id = $this->value['mch_id'] = $mch_id;
$this->key = $key;
}
public function sendRed()
{
foreach($this->value as $k=>$v){
if(empty($v)){
return $k.' 不能为空';
}
}
if(empty($this->apiclient_cert) || empty($this->apiclient_key) || empty($this->key)){
return '证书路径和key不能为空';
}
$this->MakeSign();
$xml = $this->ToXml();
$result_xml = $this->postXmlCurl($xml,$this->url);
$result = $this->FromXml($result_xml);
// $this->wirteLog($this->setMsg($result));
return $result;
}
public function getOrderStatus($order_on)
{
$url = "https://api.mch.weixin.qq.com/mmpaysptrans/query_bank";
$rand_str = md5(rand(100000,99999));
$data = array("mch_id"=>$this->mch_id,"nonce_str"=>$rand_str,'partner_trade_no'=>$order_on);
$str = "mch_id={$this->mch_id}&nonce_str={$rand_str}&partner_trade_no={$order_on}&key={$this->key}";
$sign = strtoupper(md5($str));
$data['sign'] = $sign;
$xml = "<xml>";
foreach ($data as $key=>$val) {
if (is_numeric($val)){
$xml .= "<".$key.">".$val."</".$key.">";
}else{
$xml .= "<".$key."><![CDATA[".$val."]]></".$key.">";
}
}
$xml .= "</xml>";
$result_xml = $this->postXmlCurl($xml, $url);
$result = $this->FromXml($result_xml);
return $result;
}
/**
* 输出xml字符
* @throws WxPayException
**/
public function ToXml()
{
if(!is_array($this->value)
|| count($this->value) <= 0)
{
throw new WxPayException("数组数据异常!");
}
$xml = "<xml>";
foreach ($this->value as $key=>$val)
{
if (is_numeric($val)){
$xml.="<".$key.">".$val."</".$key.">";
}else{
$xml.="<".$key."><![CDATA[".$val."]]></".$key.">";
}
}
$xml.="</xml>";
return $xml;
}
public function createXml($data)
{
if(!is_array($data)
|| count($data) <= 0)
{
throw new WxPayException("数组数据异常!");
}
$xml = "<xml>";
foreach ($data as $key=>$val)
{
if (is_numeric($val)){
$xml.="<".$key.">".$val."</".$key.">";
}else{
$xml.="<".$key."><![CDATA[".$val."]]></".$key.">";
}
}
$xml.="</xml>";
return $xml;
}
public function createMakeSign($data)
{
$buff = "";
foreach ($data as $k => $v)
{
if($k != "sign" && $v != "" && !is_array($v)){
$buff .= $k . "=" . $v . "&";
}
}
$buff = trim($buff, "&");
$buff = $buff . "&key=".$this->key;
$string = md5($buff);
//签名步骤四:所有字符转为大写
$result = strtoupper($string);
return $result;
}
/**
* 生成签名
* @return 签名,本函数不覆盖sign成员变量,如要设置签名需要调用SetSign方法赋值
*/
public function MakeSign()
{
//签名步骤一:按字典序排序参数
ksort($this->value);
$string = $this->ToUrlParams();
//签名步骤二:在string后加入KEY
$string = $string . "&key=".$this->key;
//签名步骤三:MD5加密
$string = md5($string);
//签名步骤四:所有字符转为大写
$result = strtoupper($string);
$this->value['sign'] = $result;
return $result;
}
/**
* 格式化参数格式化成url参数
*/
public function ToUrlParams()
{
$buff = "";
foreach ($this->value as $k => $v)
{
if($k != "sign" && $v != "" && !is_array($v)){
$buff .= $k . "=" . $v . "&";
}
}
$buff = trim($buff, "&");
return $buff;
}
public function FromXml($xml)
{
if(!$xml){
throw new \Exception("xml数据异常!");
}
//将XML转为array
//禁止引用外部xml实体
libxml_disable_entity_loader(true);
$this->values = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
return $this->values;
}
/**
* 以post方式提交xml到对应的接口url
*
* @param string $xml 需要post的xml数据
* @param string $url url
* @param bool $useCert 是否需要证书,默认不需要
* @param int $second url执行超时时间,默认30s
* @throws WxPayException
*/
private function postXmlCurl($xml, $url, $useCert = false, $second = 30)
{
$ch = curl_init();
//设置超时
curl_setopt($ch, CURLOPT_TIMEOUT, $second);
curl_setopt($ch,CURLOPT_URL, $url);
curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,TRUE);
curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,2);//严格校验
//设置header
curl_setopt($ch, CURLOPT_HEADER, FALSE);
//要求结果为字符串且输出到屏幕上
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
// if($useCert == true){
//设置证书
//使用证书:cert 与 key 分别属于两个.pem文件
curl_setopt($ch,CURLOPT_SSLCERTTYPE,'PEM');
curl_setopt($ch,CURLOPT_SSLCERT, $this->apiclient_key);
curl_setopt($ch,CURLOPT_SSLKEYTYPE,'PEM');
curl_setopt($ch,CURLOPT_SSLKEY, $this->apiclient_cert);
// }
//post提交方式
curl_setopt($ch, CURLOPT_POST, TRUE);
curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
//运行curl
$data = curl_exec($ch);
//返回结果
if($data){
curl_close($ch);
return $data;
} else {
$error = curl_errno($ch);
curl_close($ch);
// throw new \Exception("curl出错,错误码:$error");
}
}
public function RAS($str)
{
$public_pem = file_get_contents($this->public_pem);
openssl_public_encrypt($str,$decrypted,$public_pem,OPENSSL_PKCS1_OAEP_PADDING);
return base64_encode($decrypted);
}
public function partnerTradeNo($value)
{
$this->value['partner_trade_no'] = $value;
}
public function nonceStr($value)
{
$this->value['nonce_str'] = $value;
}
public function encBankNo($value)
{
$this->value['enc_bank_no'] = $this->RAS($value);
}
public function encTrueName($value)
{
$this->value['enc_true_name'] = $this->RAS($value);
}
public function bankCode($value)
{
$this->value['bank_code'] = $value;
}
public function amount($value)
{
$this->value['amount'] = $value;
}
public function desc($value)
{
$this->value['desc'] = $value;
}
}
<?php
require './Xcxpay/Wxyxk.php';//引入刚刚的类
$mch_id = '123456';
$key = "1111111111";
$model = new Wxyxk($mch_id,$key);
$model->apiclient_key = './Xcxpay/Certificate/Jw/apiclient_cert.pem';//证书路径 在商户平台下载 第一步获取的证书
$model->apiclient_cert = './Xcxpay/Certificate/Jw/apiclient_key.pem';//证书路径 在商户平台下载 第一步获取的证书
$model->public_pem = "./Xcxpay/Certificate/Jw/wx_public8.pem";//第二步获取的 微信RAS公匙
$model->partnerTradeNo($order_on);//订单号
$model->nonceStr(md5(rand(1000,9999)));
$model->encBankNo($employee_bank_info['bank_no']);//银行卡号
$model->encTrueName($employee_bank_info['name']);//银行卡持卡人姓名
$model->bankCode($employee_bank_info['bank_code']);//微信银行卡id 见下图
$model->amount($amount);//金额
$model->desc($declare);//订单说明
$result = $model->sendRed();