APP支付(支付宝和微信生成客户端签名及回调验签)
匿名 · 更新于 2017/6/29
- <?php
- namespace Pn\Controller;
- use Think\Controller\RestController;
- class PayController extends RestController{
- public function __construct(){
- import('Vendor.WePay.WeEncryption');
- import('Vendor.WePay.Curl');
- }
- public function Payment(){
- $arr['appid'] = $_POST['appid'];//应用id
- $arr['userdid'] = $_POST['userdid'];//登录用户id
- $arr['paytype'] = $_POST['paytype'];//支付方式
- $arr['productid'] = $_POST['productid'];//商品id
- $arr['time'] = $_POST['time'];
- $sign = $_POST['sign'];//安全验证sign
- $vsign = strrank($arr);//签名加密
- if($vsign!=$sign){
- $return['respCode'] = 1001;
- $return['msg'] = "签名错误";
- $this -> response($return,'json');//返回数据
- exit();
- }else{
- switch ($_POST["paytype"]){
- case 1://支付宝
- $data["signdata"] = $this->alipay();
- echo json_encode($data);
- break;
- case 2://微信支付
- $data = $this->wechatpay($_POST['appid'],$_POST['userdid'],$_POST['productid']);
- echo $data;
- break;
- default:
- $return["respCode"] = "error";
- $return['msg'] = "支付方式错误";
- $this -> response($return,'json');//返回数据
- exit();
- }
- }
- }
- /**
- * 微信APP客户端支付
- */
- public function wechatpay($appid,$userdid,$productid){
- $WeEncryption = new \WeEncryption(); //实例化传输类;
- switch ($productid){
- case 1://季付商品(¥100)
- $body = "陪你季付VIP";//商品描述
- $total_fee = "100.00";//总金额
- break;
- case 2://月付商品(¥50)
- $body = "陪你月付VIP";//商品描述
- $total_fee = "50.00";//总金额
- break;
- }
- $out_trade_no = date("YmdHis").rand(10,100);//商户订单号
- $spbill_create_ip = getclientip();//终端IP
- $data = array(
- 'body' => $body,
- 'out_trade_no' => $out_trade_no,
- 'total_fee' => $total_fee,
- 'spbill_create_ip' => $spbill_create_ip,
- );
- $encpt = $WeEncryption::getInstance(); //实例化签名类
- $url = "http://xxxx.com/Pay/wx_notify_verify";
- $encpt->setNotifyUrl($url); //设置异步通知地址
- $curl = new \Curl(); //实例化传输类;
- $xml_data = $encpt->sendRequest($curl, $data); //发送请求
- $postObj = $encpt->xmlToObject($xml_data); //解析返回数据
- if ($postObj === false) {
- echo 'FAIL';
- exit;
- }
- if ($postObj->return_code == 'FAIL') {
- echo $postObj->return_msg;
- } else {
- $resignData = array(
- 'appid' => $postObj->appid,
- 'partnerid' => $postObj->mch_id,
- 'prepayid' => $postObj->prepay_id,
- 'noncestr' => $postObj->nonce_str,
- 'timestamp' => time(),
- 'package' => 'Sign=WXPay'
- );
- $sign = $encpt->getClientPay($resignData);
- $resignData['sign'] = $sign;
- return json_encode($resignData);
- }
- }
- /**
- * 微信App支付回调
- */
- public function wx_notify_verify(){
- $WeEncryption = new \WeEncryption(); //实例化传输类;
- $encpt = $WeEncryption::getInstance();
- $obj = $encpt->getNotifyData();
- if ($obj === false) {
- exit;
- }
- if ($obj) {
- $data = array(
- 'appid' => $obj->appid,
- 'mch_id' => $obj->mch_id,
- 'nonce_str' => $obj->nonce_str,
- 'result_code' => $obj->result_code,
- 'openid' => $obj->openid,
- 'trade_type' => $obj->trade_type,
- 'bank_type' => $obj->bank_type,
- 'total_fee' => $obj->total_fee,
- 'cash_fee' => $obj->cash_fee,
- 'transaction_id' => $obj->transaction_id,
- 'out_trade_no' => $obj->out_trade_no,
- 'time_end' => $obj->time_end
- );
- $sign = $encpt->getSign($data);
- if ($sign == $obj->sign) {//验签通过
- $reply = "<xml>
- <return_code><![CDATA[SUCCESS]]></return_code>
- <return_msg><![CDATA[OK]]></return_msg>
- </xml>";
- echo $reply;
- exit;
- }else{
- $reply = "<xml>
- <return_code><![CDATA[FAIL]]></return_code>
- <return_msg><![CDATA[NO]]></return_msg>
- </xml>";
- echo $reply;
- exit;
- }
- }
- }
- /**
- * https://doc.open.alipay.com/doc2/detail.htm?spm=a219a.7629140.0.0.NgdeQA&treeId=59&articleId=103663&docType=1
- * 服务端生成app支付使用的参数以及签名
- * @param $params <Array>
- * @return <Array>
- */
- public function alipay(){
- $ali = array(
- 'service' => 'mobile.securitypay.pay',
- 'partner' => 'xxxx',
- '_input_charset' => 'utf-8',
- 'sign_type' => 'RSA',
- 'sign' => '',
- 'notify_url' => 'http://xxxxxxxx.com/Pay/alipay_notify_url',//回调地址
- 'out_trade_no' => date("YmdHis").rand(10,100),//商户网站唯一订单号
- 'subject' => '测试',//商品名称
- 'payment_type' => 1,//支付类型
- 'seller_id' => 'lql@jieku.com',//支付宝账号
- 'total_fee' => '0.01',//总金额
- 'body' => 'Iphone6 16G',//商品详情
- );
- $ali = self::argSort($ali);
- $str = '';
- foreach($ali as $key=>$val){
- if($key == 'sign_type' || $key == 'sign'){
- continue;
- }else{
- if($str == ''){
- $str = $key.'='.'"'.$val.'"';
- }else{
- $str = $str.'&'.$key.'='.'"'.$val.'"';
- }
- }
- }
- $sign = urlencode(self::sign($str));
- $str = $str.'&sign='.'"'.$sign.'"'.'&sign_type='.'"'.$ali['sign_type'].'"';//传给支付宝接口的数据
- return $str;
- }
- /**
- * 准备签名参数
- */
- public function argSort($para) {
- ksort($para);
- reset($para);
- return $para;
- }
- /**
- * 生成请求参数的签名
- * @param $params <Array>
- * @return <String>
- *
- */
- public function sign($data) {
- //读取私钥文件
- $priKey = file_get_contents('./Public/key/rsa_private_key.pem');//私钥文件路径
- //转换为openssl密钥,必须是没有经过pkcs8转换的私钥
- $res = openssl_get_privatekey($priKey);
- //调用openssl内置签名方法,生成签名$sign
- openssl_sign($data, $sign, $res);
- //释放资源
- openssl_free_key($res);
- //base64编码
- $sign = base64_encode($sign);
- return $sign;
- }
- /**
- * 支付宝回调地址
- */
- public function alipay_notify_url(){
- $orderpay = M("orderpay");
- $async = empty($_GET);
- $data = $async ? $_POST : $_GET;
- if (empty($data)) {
- return FALSE;
- }
- $signValid = $this->verifyParameters($data, $data["sign"]);//验签
- $notify_id = isset($data['notify_id']) ? $data['notify_id'] : NULL;
- //获取支付宝远程服务器ATN结果(验证是否是支付宝发来的消息)
- $responseTxt = 'true';
- if (!empty($notify_id)) {
- $responseTxt = $this->verifyFromServer($notify_id);
- }
- if($signValid && preg_match("/true$/i", $responseTxt)){//验证成功
- if($_POST['trade_status'] == 'TRADE_FINISHED'){
- $_POST["createdatetime"] = time();
- if($orderpay->create()){
- $orderpay->add();
- echo "success";
- }else{
- echo "fail";//验证失败
- }
- }
- else if ($_POST['trade_status'] == 'TRADE_SUCCESS') {
- $_POST["createdatetime"] = time();
- if($orderpay->create()){
- $orderpay->add();
- echo "success";
- }else{
- echo "fail";//验证失败
- }
- }
- }else{
- echo "fail";//验证失败
- }
- }
- /**
- * 将剩下参数进行url_decode, 然后进行字典排序,组成字符串,得到待签名字符串
- * @param unknown $params
- * @param unknown $sign
- * @return boolean
- */
- function verifyParameters($params, $sign) {
- $public_key_path = './Public/key/alipay_public_key.pem';//公钥(合作伙伴)
- $params = $this->filterSignParameter($params);
- ksort($params);
- reset($params);
- $content = urldecode(http_build_query($params));
- return $this->rsaVerify($content, $public_key_path, $sign);
- }
- /**
- * 过滤参数,去除sign/sign_type参数
- * @param $params
- * @return <Array>
- */
- function filterSignParameter($params) {
- $result = array();
- foreach ($params as $key => $value) {
- if ($key != 'sign' && $key != 'sign_type' && $value) {
- $result[$key] = $value;
- }
- }
- return $result;
- }
- /**
- * RSA验签,注意验签的公钥是支付宝的公钥,不是自己生成的rsa公钥,可以在淘宝的demo中获得
- * @param $data string 待签名数据
- * @param $ali_public_key_path string 支付宝的公钥文件路径
- * @param $sign string 要校对的的签名结果
- * @return <Boolean> 验证结果
- * @throws Exception
- */
- function rsaVerify($data, $ali_public_key_path, $sign) {
- $pubKey = file_get_contents($ali_public_key_path);
- $res = openssl_get_publickey($pubKey);
- if(!$res){
- throw new \Exception('公钥格式错误');
- }
- $result = (bool)openssl_verify($data, base64_decode($sign), $res);
- openssl_free_key($res);
- return $result;
- }
- function verifyFromServer($notify_id) {
- $transport = "http";
- $partner = "xxxxxxxxxxx";
- $cacert = "./Public/key/cacert.pem";
- $transport = strtolower(trim($transport));
- $partner = trim($partner);
- $veryfy_url = "http://notify.alipay.com/trade/notify_query.do?partner=$partner¬ify_id=$notify_id";
- $curl = curl_init($veryfy_url);
- curl_setopt($curl, CURLOPT_HEADER, 0); // 过滤HTTP头
- curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, true);//SSL证书认证
- curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 2);//严格认证
- curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);// 显示输出结果
- curl_setopt($curl, CURLOPT_CAINFO, $cacert);//证书地址
- $responseText = curl_exec($curl);
- // var_dump( curl_error($curl) );//如果执行curl过程中出现异常,可打开此开关,以便查看异常内容
- curl_close($curl);
- return $responseText;
- }
- /**格式化公钥
- * $pubKey PKCS#1格式的公钥串
- * return pem格式公钥, 可以保存为.pem文件
- */
- function formatPubKey($pubKey) {
- $fKey = "-----BEGIN PUBLIC KEY-----\n";
- $len = strlen($pubKey);
- for($i = 0; $i < $len; ) {
- $fKey = $fKey . substr($pubKey, $i, 64) . "\n";
- $i += 64;
- }
- $fKey .= "-----END PUBLIC KEY-----";
- return $fKey;
- }
- }
