背景:商户系统需要一个集成微信支付
这里放一个 PHP的demo,这个是在一个视频上面学到的,放在这里以后可能会用到。
Base类——实际项目继承至这里
<?php
class Base
{
//key只能从商户后台获取
const KEY = '0D0352B99D829E9427446A9360D27108'; //密钥
const CODEURL = 'https://open.weixin.qq.com/connect/oauth2/authorize?';//获取openid
const OPENIDURL = 'https://api.weixin.qq.com/sns/oauth2/access_token?';//获取openid
const APPID = 'wx252bfdb5af9f6a78';
const SECRET = 'be76a8345a964ef3bbcde5fe221da455';
const MCHID = '1517459461';//商户号
const UNURL = 'https://api.mch.weixin.qq.com/pay/unifiedorder';//微信支付请求的地址
//生成签名
public function getSign($arr){
//去除空值
$arr = array_filter($arr);
//剔除sign
if(isset($arr['sign'])){
unset($arr['sign']);
}
//按照键名顺序排序
ksort($arr);
//生成url格式的字符串
$str = $this->arrToUrl($arr) . 'key=' . self::KEY;
return strtoupper(md5($str));
}
//获取带签名的数组
public function setSign($arr){
$arr['sign'] = $this->getSign($arr);;
return $arr;
}
public function arrToUrl($arr){
$stringA = '';
foreach($arr as $k => $v) {
$stringA .= $k . '=' . $v . '&';
}
return $stringA;
}
//验证签名
public function chekSign($arr){
$sign = $this->getSign($arr);
if($sign == $arr['sign']){
return true;
}else{
return false;
}
}
//获取用户id
public function getOpenId(){
if(isset($_SESSION['openid'])){
return $_SESSION['openid'];
}else{
//1.用户访问一个地址 先获取到code
if(!isset($_GET['code'])){
//print_r($_SERVER);
$redurl = $_SERVER['REQUEST_SCHEME'] . '://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
// $url = self::CODEURL . "appid=" .self::APPID ."&redirect_uri={$redurl}&response_type=code&scope=snsapi_base&state=STATE#wechat_redirect";
$url = self::CODEURL . "appid=" .self::APPID ."&redirect_uri=http://yueche.linfenshiyaoshenjiaxiao.cn/paytest/index.php&response_type=code&scope=snsapi_base&state=STATE#wechat_redirect";
//构建跳转地址 跳转
header("location:{$url}");
}else{
//2.根据code获取到openid
//调用接口获取openid
$openidurl = self::OPENIDURL . "appid=" . self::APPID . "&secret=".self::SECRET . "&code=" . $_GET['code'] . "&grant_type=authorization_code";
$data = file_get_contents($openidurl);
$arr = json_decode($data,true);
$_SESSION['openid'] = $arr['openid'];
return $_SESSION['openid'];
}
}
}
//调用”统一下单“api接口,传递订单号
public function unifiedOrder($oid){
// 1.构建原始数据
// 2.加入签名
// 3.将数据转换为XML
// 4.发送XML格式的数据到接口地址
$params = [
'appid'=> self::APPID,
'mch_id'=> self::MCHID,
//随机数组为当前加密时间戳
'nonce_str'=>md5(time()),
//驾校服务支付测试
'body'=>'4444',
//内部订单号
'out_trade_no'=>$oid,
//总价
'total_fee'=> 2,
//客户端ip
'spbill_create_ip'=>$_SERVER['REMOTE_ADDR'],
//支付通知地址
'notify_url'=> 'https://609laolu.sc2yun.com/paytest/notify.php',
//支付类型为公众号支付,选择jsapi
'trade_type'=>'JSAPI',
//产品id
'product_id'=>$oid,
//获取到的openid
'openid'=> $this->getOpenId()
];
//加入签名
$params = $this->setSign($params);
//转xml格式数据
$xmldata = $this->ArrToXml($params);
//存储数据到log下
$this->logs('log.txt', $xmldata);
$resdata = $this->postXml(self::UNURL, $xmldata);
$arr = $this->XmlToArr($resdata);
return $arr;
}
//获取预支付id,传递订单号
public function getPrepayId($oid){
$arr = $this->unifiedOrder($oid);
return $arr['prepay_id'];
}
//获取公众号支付所需要的js格式的数据
public function getJsParams($prepay_id){
$params = [
'appId' => self::APPID,
'timeStamp' => (string)time(),
'nonceStr' => md5(time()),
'package' =>'prepay_id=' . $prepay_id,
'signType' =>'MD5',
//'paySign' => $this->getSign($params)。 数组还未定义完成,放到外面写
];
$params['paySign'] = $this->getSign($params);
echo "<pre>";
var_dump($params);
return json_encode($params);
}
//数组转xml
public function ArrToXml($arr)
{
if(!is_array($arr) || count($arr) == 0) return '';
$xml = "<xml>";
foreach ($arr as $key=>$val)
{
if (is_numeric($val)){
$xml.="<".$key.">".$val."</".$key.">";
}else{
$xml.="<".$key."><![CDATA[".$val."]]></".$key.">";
}
}
$xml.="</xml>";
return $xml;
}
//xml转数组
public function XmlToArr($xml)
{
if($xml == '') return '';
libxml_disable_entity_loader(true);
$arr = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
return $arr;
}
//存储文件的方法(blogs)
public function logs($filename,$data){
file_put_contents('./logs/' . $filename, $data);
}
//上传信息的方法(blogs)
public function postXml($url,$postfields){
$ch = curl_init();
$params[CURLOPT_URL] = $url; //请求url地址
$params[CURLOPT_HEADER] = false; //是否返回响应头信息
$params[CURLOPT_RETURNTRANSFER] = true; //是否将结果返回
$params[CURLOPT_FOLLOWLOCATION] = true; //是否重定向
$params[CURLOPT_POST] = true;
$params[CURLOPT_POSTFIELDS] = $postfields;
//禁用证书验证
$params[CURLOPT_SSL_VERIFYPEER] = false;
$params[CURLOPT_SSL_VERIFYHOST] = false;
curl_setopt_array($ch, $params); //传入curl参数
$content = curl_exec($ch); //执行
curl_close($ch); //关闭连接
return $content;
}
//获取post过来的数据
public function getPost(){
return file_get_contents('php://input');
}
}
测试类——项目从这里开始
<?php
session_start();
include './Base.php';
class WeiXinPay extends Base
{
public function __construct() {
}
}
$obj = new WeiXinPay();
$prepay_id = $obj->getPrepayId(time().rand(11,99));
//echo $prepay_id;
$json = $obj->getJsParams($prepay_id);
?>
<script>
function onBridgeReady(){
WeixinJSBridge.invoke(
'getBrandWCPayRequest', <?php echo $json;?>,
function(res){
if(res.err_msg == "get_brand_wcpay_request:ok" ) {} // 使用以上方式判断前端返回,微信团队郑重提示:res.err_msg将在用户支付成功后返回 ok,但并不保证它绝对可靠。
}
);
}
if (typeof WeixinJSBridge == "undefined"){
if( document.addEventListener){
document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false);
}else if (document.attachEvent){
document.attachEvent('WeixinJSBridgeReady', onBridgeReady);
document.attachEvent('onWeixinJSBridgeReady', onBridgeReady);
}
}else{
onBridgeReady();
}
</script>
还有一个支付成功后微信请求我们服务器的一个页面
<?php
include './Base.php';
//1.获取通知数据 ->转换为数组
//2.验证签名 (使用以前的方法)
//3.验证业务结果 (return_code 和 result_code)
//4.验证订单号和金额 (out_trade_no total_fee)
//5.记录日志 修改订单状态 给用户发货
class Notify extends Base
{
public function __construct() {
$xmlData = $this->getPost();
$arr = $this->XmlToArr($xmlData);
if($this->chekSign($arr)){
if($arr['return_code'] == 'SUCCESS' && $arr['result_code'] == 'SUCCESS'){
//生产环境需要根据订单号来查询价格
if($arr['total_fee'] == 2){
$this->logs('stat.txt', '交易成功!'); //更改订单状态
$returnParams = [
'return_code' => 'SUCCESS',
'return_msg' => 'OK'
];
echo $this->ArrToXml($returnParams);
}else{
$this->logs('stat.txt', '金额有误!');
}
}else{
$this->logs('stat.txt', '业务结果不正确!');
}
}else{
$this->logs('stat.txt', '签名失败!');
}
}
}
$obj = new Notify();