Discuz!官方免费开源建站系统

 找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索

[分享] 微信网页支付接口的主逻辑封装

[复制链接]
monkeye 发表于 2015-10-15 10:16:30 | 显示全部楼层 |阅读模式
Part1 前提条件
使用微信支付必须拥有以下字段, 其中partnerId,partnerKey和paySignKey需要开通微信支付方可获得:

appId - 公众号身份的唯一标识
appSecret - 公众平台接口API密钥
partnerId - 财付通商户身份的标识
partnerKey - 财付通商户权限密钥 Key
paySignKey - 公众号支付请求中用于加密的密钥Key,对应于支付场景中的appKey值
拿到以上字段后在WXConstant.php中进行配置。

Part2 接口封装
微信支付接口文档:使用公众号发起支付请求

1. web页面调用微信支付
调用JS API支付接口getBrandWCPayRequest()能够调出微信支付的页面:

/
  1. / js 调用微信支付页面
  2. WeixinJSBridge.invoke('getBrandWCPayRequest', {
  3.     appId: appId, // 公众号id
  4.     timeStamp: timeStamp, // 时间戳
  5.     nonceStr: nonceStr, // 随机字符串
  6.     package: package, // 订单详情扩展字符串
  7.     signType: signType, // 签名方式
  8.     paySign: paySign // 签名
  9. }, function(res) {
  10.     if (res.err_msg != 'get_brand_wcpay_request:ok') {
  11.         alert('支付失败!');
  12.     } else {
  13.         alert('支付成功!');
  14.     }
  15. });
复制代码

以上代码中,涉及到appId,timeStamp,nonceStr,package,signType和paySign六个参数, 其中主要工作在于生成订单详情扩展字符串package和签名paySign:

1.1 生成订单详情(package)扩展字符串
  1. $wxpay = WXPay::factory();
  2. $package = $wxpay->getPackage($orderData);
复制代码

订单详情信息包含有发起支付请求的商户信息和交易信息,重点看其中的3个参数:

out_trade_no - 商户订单号 此订单号由调用者即商户自己生成,在商户内全局唯一来标识该比交易, 微信后台通知商户支付成功时,会将此订单号发送给商户
total_fee - 订单总金额 此次交易的总金额
notify_url - 通知 URL 此url由商户提供,在支付完成后微信将通过此url通知商户订单的支付
1.2 生成支付签名
$paySign = $wxpay->getPaySign();
2. 接收支付结果通知
用户在成功完成支付后,微信后台通知( POST)商户服务器(notify_url)支付结果, 在微信发过来的数据中重点关注以下参数值:

total_fee - 订单总金额
transaction_id - 订单号, 由微信方生成
out_trade_no - 商户订单号,1.1中提到的商户订单号
time_end - 支付完成时间
sign - 签名
接收到微信通知后,在验证签名后,就可以根据out_trade_no对相应的订单进行状态更新了。

  1. WXPay::factory()->receivePayNotify();
复制代码

3. 发货通知
在收到支付通知后,商户要按时发货,并使用发货通知接口将相关信息同步到微信后台。若微信平台在规定时间内没有收到,将视作发货超时处理。 发货时间限制:虚拟、服务类 24 小时内,实物类 72小时内。

  1. WXPay::factory()->deliverNotify($postData)
复制代码

4. 查询订单
商户在预期时间内未收到支付结果通知,可以通过此接口查询订单的支付状态

  1. WXPay::factory()->orderQuery($postData)
复制代码

5. 接收告警通知
微信后台会向商户推送告警 通知,包括发货延迟 、调用失败、通知失败等情况

  1. WXPay::factory()->receiveAlert($postXML)
复制代码

6. 接收用户维权信息通知
接入微信支付的商户都必须接入用户维权系统,微信将用户投诉内容通过此api发送给商户

  1. WXPay::factory()->receivePayFeedback($postXML = '')
复制代码

7. 更新用户维权处理结果
商户可以通过此api调用,标记客户的投诉处理状态

  1. WXPay::factory()->updatePayFeedback(($openId, $feedbackId))
复制代码



  1. <?php
  2. /***
  3. * 微信支付类
  4. * 接口调试:http://mp.weixin.qq.com/debug/cgi-bin/readtmpl?t=pay/index
  5. *
  6. * @author zhanghuichao@tencent.com
  7. *
  8. */
  9. class WXPay
  10. {
  11.         private $appId;
  12.         private $appSecret;
  13.         private $partnerId;
  14.         private $partnerKey;
  15.         private $paySignKey;
  16.        
  17.         private $timeStamp = '';
  18.         private $nonceStr = '';
  19.         private $package = '';
  20.         private $paySign = '';
  21.        
  22.         const SIGN_TYPE = 'SHA1';
  23.         const INPUT_CHARSET = 'UTF-8';
  24.        
  25.         /*
  26.          * 发货通知接口参数
  27.          */
  28.         protected $deliverNotifyParams = array(
  29.                         'appid' =>  array(
  30.                                         'isRequired' => 1,
  31.                                         'defaultValue' => WXConstant::APP_ID,
  32.                                         'desc' => '公众平台账户的 AppId'
  33.                         ),
  34.                         'openid' =>  array(
  35.                                         'isRequired' => 1,
  36.                                         'desc' => '购买用户的 OpenId'
  37.                         ),
  38.                         'transid' =>  array(
  39.                                         'isRequired' => 1,
  40.                                         'desc' => '交易单号'
  41.                         ),
  42.                         'out_trade_no' =>  array(
  43.                                         'isRequired' => 1,
  44.                                         'desc' => '第三方订单号'
  45.                         ),
  46.                         'deliver_timestamp' =>  array(
  47.                                         'isRequired' => 1,
  48.                                         'desc' => '发货时间戳,这里指的是 Linux 时间戳'
  49.                         ),
  50.                         'deliver_status' =>  array(
  51.                                         'isRequired' => 1,
  52.                                         'desc' => '发货状态,1 表明成功,0 表明失败,失败时需要在 deliver_msg 填上失败原因;'
  53.                         ),
  54.                         'deliver_msg' =>  array(
  55.                                         'isRequired' => 1,
  56.                                         'desc' => '发货状态信息'
  57.                         ),
  58.                         'sign_method' =>  array(
  59.                                         'isRequired' => 1,
  60.                                         'defaultValue' => 'sha1',
  61.                                         'desc' => '发货状态信息'
  62.                         ),
  63.         );
  64.        
  65.         /*
  66.          * 支付回调通知接口参数
  67.          */
  68.         protected $notifyParams = array(
  69.                         'sign_type' => array(
  70.                                         'isRequired' => 0,
  71.                                         'name' => '签名方式',
  72.                                         'desc' => '签名类型,取值:MD5、RSA,默 认:MD5; String(8)'
  73.                         ),
  74.                         'input_charset' => array(
  75.                                         'isRequired' => 0,
  76.                                         'name' => '签名方式',
  77.                                         'desc' => '字符编码,取值:GBK、UTF-8,默 认:GBK。; String(8)'
  78.                         ),
  79.                         'sign' => array(
  80.                                         'isRequired' => 1,
  81.                                         'name' => '签名',
  82.                                         'desc' => '签名; String(32)'
  83.                         ),
  84.                         'trade_mode' => array(
  85.                                         'isRequired' => 1,
  86.                                         'name' => '交易模式',
  87.                                         'desc' => '1-即时到账 其他保留; Int'
  88.                         ),
  89.                         'trade_state' => array(
  90.                                         'isRequired' => 1,
  91.                                         'name' => '交易状态',
  92.                                         'desc' => '支付结果: 0—成功 其他保留;Int'
  93.                         ),
  94.                         'partner' => array(
  95.                                         'isRequired' => 1,
  96.                                         'name' => '商户号',
  97.                                         'desc' => '商户号,也即之前步骤的 partnerid, 由微信统一分配的 10 位正整数 (120XXXXXXX)号; String(10)'
  98.                         ),
  99.                         'bank_type' => array(
  100.                                         'isRequired' => 1,
  101.                                         'name' => '付款银行',
  102.                                         'desc' => '银行类型,在微信中使用 WX        ; String(16)'
  103.                         ),
  104.                         'bank_billno' => array(
  105.                                         'isRequired' => 0,
  106.                                         'name' => '银行订单号',
  107.                                         'desc' => '银行订单号; String(32)'
  108.                         ),
  109.                         'total_fee' => array(
  110.                                         'isRequired' => 1,
  111.                                         'name' => '总金额',
  112.                                         'desc' => '支付金额,单位为分,如果 discount 有值,通知的 total_fee + discount = 请求的 total_fee; Int'
  113.                         ),
  114.                         'fee_type' => array(
  115.                                         'isRequired' => 1,
  116.                                         'name' => '币种',
  117.                                         'desc' => '现金支付币种 ,目前只支持人民币 , 默认值是 1-人民币; Int'
  118.                         ),
  119.                         'notify_id' => array(
  120.                                         'isRequired' => 1,
  121.                                         'name' => '通知ID',
  122.                                         'desc' => '支付结果通知 id,对于某些特定商 户,只返回通知 id,要求商户据此 查询交易结果; String(128)'
  123.                         ),
  124.                         'transaction_id' => array(
  125.                                         'isRequired' => 1,
  126.                                         'name' => '订单号',
  127.                                         'desc' => '交易号,28 位长的数值,其中前 10 位为商户号,之后 8 位为订单产生 的日期,如 20090415,最后 10 位 是流水号。; String(28)'
  128.                         ),
  129.                         'out_trade_no' => array(
  130.                                         'isRequired' => 1,
  131.                                         'name' => '商户订单号',
  132.                                         'desc' => '商户系统的订单号,与请求一致; String(32)'
  133.                         ),
  134.                         'attach' => array(
  135.                                         'isRequired' => 0,
  136.                                         'name' => '商户数据包',
  137.                                         'desc' => '商户数据包,原样返回,空参数不传递; String(127)'
  138.                         ),
  139.                         'time_end' => array(
  140.                                         'isRequired' => 1,
  141.                                         'name' => '支付完成时间',
  142.                                         'desc' => '支付完成时间,格式 为 yyyyMMddhhmmss,如 2009 年 12 月27日9点10分10秒表示 为 20091227091010 。时区为 GMT+8 beijing。; String(14)'
  143.                         ),
  144.                         'transport_fee' => array(
  145.                                         'isRequired' => 0,
  146.                                         'name' => '物流费用',
  147.                                         'desc' => '物流费用,单位分,默认 0。如果 有值,必须保证 transport_fee + product_fee = total_fee; Int'
  148.                         ),
  149.                         'product_fee' => array(
  150.                                         'isRequired' => 0,
  151.                                         'name' => '物品费用',
  152.                                         'desc' => '物品费用,单位分。如果有值,必 证保须 transport_fee +product_fee=total_fee; Int'
  153.                         ),
  154.                         'discount' => array(
  155.                                         'isRequired' => 0,
  156.                                         'name' => '物品费用',
  157.                                         'desc' => '折扣价格,单位分,如果有值,通 知的 total_fee + discount = 请求 的 total_fee; Int'
  158.                         ),
  159.         );
  160.        
  161.         /*
  162.          * 订单详情package参数
  163.          */
  164.         protected $packageParams = array(
  165.                                 'bank_type' => array(
  166.                                                 'isRequired' => 1,
  167.                                                 'defaultValue' => 'WX',
  168.                                                 'name' => '银行通道类型',
  169.                                                 'desc' => '字符串类型, 为定固"WX" ,注意 大写'
  170.                                 ),
  171.                                 'body' => array(
  172.                                                 'isRequired' => 1,
  173.                                                 'name' => '商品描述',
  174.                                                 'desc' => '字符串类型, 128 字节以下'
  175.                                 ),
  176.                                 'attach' => array(
  177.                                                 'isRequired' => 0,
  178.                                                 'name' => '附加数据',
  179.                                                 'desc' => '附加数据,原样返回;字符串类型, 128 字节以下'
  180.                                 ),
  181.                                 'partner' => array(
  182.                                                 'isRequired' => 1,
  183.                                                 'defaultValue' => WXConstant::PARTNER_ID,
  184.                                                 'name' => '商户号',
  185.                                                 'desc' => '注册时分配的财付通商户号 partnerId;字符串类型'
  186.                                 ),
  187.                                 'out_trade_no' => array(
  188.                                                 'isRequired' => 1,
  189.                                                 'name' => '商户订单号',
  190.                                                 'desc' => '商户系统内部的订单号,32 个字符内、可包含字 母;确保在商户系统唯一;字符串类型, 32字节以下'
  191.                                 ),
  192.                                 'total_fee' => array(
  193.                                                 'isRequired' => 1,
  194.                                                 'name' => '订单总金额',
  195.                                                 'desc' => '字符串类型'
  196.                                 ),
  197.                                 'fee_type' => array(
  198.                                                 'isRequired' => 1,
  199.                                                 'defaultValue' => 1,
  200.                                                 'name' => '支付币种',
  201.                                                 'desc' => '字符串类型,默认值是1;暂只支持1'
  202.                                 ),
  203.                                 'notify_url' => array(
  204.                                                 'isRequired' => 1,
  205.                                                 'defaultValue' => WXConstant::PAY_NOTIFY_URL,
  206.                                                 'name' => '通知URL',
  207.                                                 'desc' => '字符串类型,在支付完成后,接收微信通知支付结果的 URL, 需给绝对路径, 255字符内'
  208.                                 ),
  209.                                 'spbill_create_ip' => array(
  210.                                                 'isRequired' => 1,
  211.                                                 'name' => '订单生成的机器IP',
  212.                                                 'desc' => '字符串类型,指用户浏览器端 IP,不是商户服务器 IP,格式为 IPV4; 15字符内'
  213.                                 ),
  214.                                 'time_start' => array(
  215.                                                 'isRequired' => 0,
  216.                                                 'name' => '交易起始时间',
  217.                                                 'desc' => '字符串类型,订单生成时间,格式为 yyyyMMddHHmmss,如 2009年12月25日9点10分10秒表示 为 20091225091010,时区为 GMT+8 beijing;该时间取自商户服务器; 14字符内'
  218.                                 ),
  219.                                 'time_expire' => array(
  220.                                                 'isRequired' => 0,
  221.                                                 'name' => '交易结束时间',
  222.                                                 'desc' => '字符串类型,订单生成时间,格式为 yyyyMMddHHmmss,如 2009年12月25日9点10分10秒表示 为 20091225091010,时区为 GMT+8 beijing;该时间取自商户服务器; 14字符内'
  223.                                 ),
  224.                                 'transport_fee' => array(
  225.                                                 'isRequired' => 0,
  226.                                                 'name' => '物流费用',
  227.                                                 'desc' => '字符串类型,物流费用,单位为分。如果有值,必须保 证 transport_fee + product_fee=total_fee;'
  228.                                 ),
  229.                                 'product_fee' => array(
  230.                                                 'isRequired' => 0,
  231.                                                 'name' => '商品费用',
  232.                                                 'desc' => '字符串类型,物流费用,单位为分。如果有值,必须保 证 transport_fee + product_fee=total_fee;'
  233.                                 ),
  234.                                 'goods_tag' => array(
  235.                                                 'isRequired' => 0,
  236.                                                 'name' => '商品标记',
  237.                                                 'desc' => '字符串类型,商品标记,优惠券时可能用到'
  238.                                 ),
  239.                                 'input_charset' => array(
  240.                                                 'isRequired' => 1,
  241.                                                 'defaultValue' => self::INPUT_CHARSET,
  242.                                                 'name' => '传入参数字符 编码',
  243.                                                 'desc' => '字符串类型,取值范围:GBK、UTF-8,默认:GBK'
  244.                                 ),
  245.                 );
  246.        
  247.         public static function factory()
  248.         {
  249.                 return new self();
  250.         }
  251.        
  252.         public function __construct()
  253.         {
  254.                 // @todo validate these values
  255.                 $this->appId = WXConstant::APP_ID;
  256.                 $this->appSecret = WXConstant::APP_SECRET;
  257.                 $this->partnerId = WXConstant::PARTNER_ID;
  258.                 $this->partnerKey = WXConstant::PARTNER_KEY;
  259.                 $this->paySignKey = WXConstant::PAY_SIGN_KEY;
  260.         }
  261.        
  262.         /**
  263.          * 获取jsapi支付请求json
  264.          * @return string
  265.          */
  266.         public function getBrandWCPayRequestParam($orderData)
  267.         {
  268.                 $this->timeStamp = $this->getTimeStamp();
  269.                 $this->nonceStr = $this->getNoncestr();
  270.                 $this->package = $this->getPackage($orderData);
  271.                 $this->paySign = $this->getPaySign();
  272.                 $params = array(
  273.                                 'appId' => $this->appId,
  274.                                 'timeStamp' => (string)$this->timeStamp,
  275.                                 'nonceStr' => $this->nonceStr,
  276.                                 'package' => $this->package,
  277.                                 'signType' => self::SIGN_TYPE,
  278.                                 'paySign' => $this->paySign,
  279.                                 'outTradeNo' => $orderData['out_trade_no'],
  280.                 );
  281.                
  282.                 return $params;
  283.         }
  284.        
  285.         /**
  286.          * 获取支付签名
  287.          */
  288.         public function getPaySign($data = '')
  289.         {
  290.                 // @todo 如果$data为空,则要检查$this->nonceStr, $this->package, $this->timeStamp是否为空
  291.                 $params = array(
  292.                                 'appid' => $this->appId,
  293.                                 'appkey' => $this->paySignKey,
  294.                                 'noncestr' => isset($data['noncestr']) ? $data['noncestr'] : $this->nonceStr,
  295.                                 'package' => isset($data['package']) ? $data['package'] : $this->package,
  296.                                 'timestamp' => isset($data['timestamp']) ? $data['timestamp'] : $this->timeStamp,
  297.                 );
  298.                
  299.                 return $this->createPaySign($params);
  300.         }
  301.        
  302.         /**
  303.          * 支付签名算法
  304.          * @param unknown $params
  305.          * @return string
  306.          */
  307.         public function createPaySign($params)
  308.         {
  309.                 ksort($params);
  310.                 $string1 = $this->httpBuildStr($params);
  311.                 $paySign = sha1($string1);
  312.                
  313.                 return $paySign;
  314.         }
  315.        
  316.         /**
  317.          * 获取订单详情(package)扩展字符串
  318.          * @return string
  319.          */
  320.         public function getPackage($orderData)
  321.         {
  322.                 // 构造参数数组
  323.                 $myPackageParams = array();
  324.                 foreach ($this->packageParams as $key => $val) {
  325.                         if ($val['isRequired']) {
  326.                                 $myPackageParams[$key] = isset($orderData[$key]) ? $orderData[$key] : (isset($val['defaultValue']) ? $val['defaultValue'] : '');
  327.                                 if (!$myPackageParams[$key]) {
  328.                                         throw new WXPayException("Please specify value for $key", -5000);
  329.                                 }
  330.                         } else {
  331.                                 if (isset($orderData[$key]) && $orderData[$key]) {
  332.                                         $myPackageParams[$key] = $orderData[$key];
  333.                                 }
  334.                         }
  335.                 }
  336.                
  337.                 // 构造sign
  338.                 $sign = $this->createPackageSign(&$myPackageParams);
  339.                
  340.                 // 构造urlencoded params string
  341.                 $paramsStringEncoded = http_build_query($myPackageParams);
  342.                
  343.                 // 拼接
  344.                 $return = $paramsStringEncoded . '&sign=' . $sign;
  345.                
  346.                 return $return;
  347.         }
  348.        
  349.         /**
  350.          * 创建订单详情package签名
  351.          * @param unknown $packageParams
  352.          * @return string
  353.          */
  354.         public function createPackageSign($packageParams)
  355.         {
  356.                 ksort($packageParams);
  357.                 $paramsString = $this->httpBuildStr($packageParams);
  358.                 $sign = strtoupper(md5($paramsString . '&key=' . WXConstant::PARTNER_KEY));
  359.                
  360.                 return $sign;
  361.         }
  362.        
  363.         /**
  364.          * 拼接query字符串
  365.          * 相当于没有urlencode功能的http_build_query()
  366.          * @param unknown $params
  367.          * @return string
  368.          */
  369.         private function httpBuildStr($params)
  370.         {
  371.                 $return = '';
  372.                 foreach ($params as $key => $val) {
  373.                         $return[] = implode('=', array($key, $val));
  374.                 }
  375.                 $return = implode('&', $return);
  376.                
  377.                 return $return;
  378.         }
  379.        
  380.         private function getTimeStamp()
  381.         {
  382.                 return time();
  383.         }
  384.        
  385.         /**
  386.          * 获取随机字符串
  387.          * 商户生成的随机字符串;取值范 围:长度为 32 个字符以下。由商户生成后传入。取值范围:32 字符以下
  388.          * @return string
  389.          */
  390.         private function getNoncestr($length = 16)
  391.         {
  392.                 $chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
  393.                 $str ="";
  394.                 for ( $i = 0; $i < $length; $i++ )  {
  395.                         $str.= substr($chars, mt_rand(0, strlen($chars)-1), 1);
  396.                 }
  397.                
  398.                 return $str;
  399.         }
  400.        
  401.         /**
  402.          * 接收微信的支付回调通知
  403.          *
  404. data from wx notify:
  405. array (
  406.         '$_GET' => array (
  407.                 'bank_billno' => '201409028971856',
  408.                 'bank_type' => '3006',
  409.                 'discount' => '0',
  410.                 'fee_type' => '1',
  411.                 'input_charset' => 'UTF-8',
  412.                 'notify_id' => 'qZ6Aae9b0UKIxhbQrM8tRZ1EVUJzAu14yvcTu_URSbzAnBkJD-nu218tG8sISJCSccacsSPOey1NQhopDUdzhNqb-s0Zpe2i',
  413.                 'out_trade_no' => 'audia354058517b30b1',
  414.                 'partner' => '1220730901',
  415.                 'product_fee' => '1',
  416.                 'sign' => 'AD7342AEB7B043E7E3DEF2BF3CCFF7CD',
  417.                 'sign_type' => 'MD5',
  418.                 'time_end' => '20140902165147',
  419.                 'total_fee' => '1',
  420.                 'trade_mode' => '1',
  421.                 'trade_state' => '0',
  422.                 'transaction_id' => '1220730901201409023179351860',
  423.                 'transport_fee' => '0',
  424.         ),
  425.         'postData' => ' 1 1409647907 ',
  426. )
  427.          */
  428.         public function receivePayNotify($notifyData = '')
  429.         {
  430.                 // 读取数据
  431.                 if (!$notifyData) {
  432.                         $postData = file_get_contents('php://input');
  433.                         $notifyData = array(
  434.                                         '$_GET' => $_GET,
  435.                                         'postData' => $postData,
  436.                         );
  437.                 }
  438.                
  439.                 // 日志
  440.                 WXBasic::factory()->doLog($notifyData, __METHOD__, 'mp.weixin.qq.com');
  441.                
  442.                 // 校验签名
  443.                 $notifySign = $notifyData['$_GET']['sign'];
  444.                 unset($notifyData['$_GET']['sign']);
  445.                 $mySign = $this->createPackageSign($notifyData['$_GET']);
  446.                 if ($notifySign != $mySign) {
  447.                         throw new WXPayException('notify sign validate failed:' . '$notifySign=' . $notifySign . ';$mySign='. $mySign, WXPayException::NOTIFY_SIGN_VALIDATE_FAILED);
  448.                 }
  449.                
  450.                 // 验证接收到的订单信息
  451.                 $notifyOutTradeNo = $notifyData['$_GET']['out_trade_no'];
  452.                 $notifyTradeState = $notifyData['$_GET']['trade_state'];
  453.                 $notifyTotalFee = $notifyData['$_GET']['total_fee'];
  454.                 if ($notifyTradeState != 0) {
  455.                         // @todo 告警
  456.                 }
  457.                
  458.                 // 比对订单数据$myPackage
  459.                 $myPackage = Order::factory()->selectByOutTradeNo($notifyOutTradeNo);
  460.                 if (!$myPackage) {
  461.                         throw new WXPayException('notify out_trade_no not exist: ' . '$notifyOutTradeNo=' . $notifyOutTradeNo, WXPayException::NOTIFY_OUT_TRADE_NO_NOT_EXIST);
  462.                 }
  463.                 if ($notifyTotalFee != $myPackage['FTotalFee']) {
  464.                         throw new WXPayException("notify total_fee incorrect:notifyTotalFee=$notifyTotalFee,myTotalFee={$myPackage['FTotalFee']}", WXPayException::NOTIFY_TOTAL_FEE_INCORRECT);
  465.                 }
  466.                
  467.                
  468.                 // 更新本地订单信息
  469.                 $notifyData = array(
  470.                                 'FTimeEnd' => isset($notifyData['$_GET']['time_end']) ? $notifyData['$_GET']['time_end'] : '',
  471.                                 'FTradeState' => isset($notifyData['$_GET']['trade_state']) ? $notifyData['$_GET']['trade_state'] : '',
  472.                                 'FBankBillNo' => isset($notifyData['$_GET']['bank_billno']) ? $notifyData['$_GET']['bank_billno'] : '',
  473.                                 'FBankType' => isset($notifyData['$_GET']['bank_type']) ? $notifyData['$_GET']['bank_type'] : '',
  474.                                 'FTransactionId' => isset($notifyData['$_GET']['transaction_id']) ? $notifyData['$_GET']['transaction_id'] : '',
  475.                                 'FNotifyId' => isset($notifyData['$_GET']['notify_id']) ? $notifyData['$_GET']['notify_id'] : '',
  476.                                 'FTimePayNotify' => date('Y-m-d H:i:s'),
  477.                 );
  478.                 Order::factory()->updateByOutTradeNo($notifyOutTradeNo, $notifyData);
  479.                
  480.                
  481.                 // 应答
  482.                 return 'success';
  483.         }
  484.        
  485.         /**
  486.          * 发货通知 delivernotify
  487.          * 通知微信已发货
  488.          *
  489.          * 第三方在收到最终支付通知之后,调用发货通知 API 告知微信后台该订单的发货状态。
  490.          * 发货时间限制:虚拟、服务类 24 小时内,实物类 72 小时内
  491.          * 若微信平台在规定时间内没有收到,将视作发货超时处理。
  492.          *
  493.          * 微信返回数据:
  494.          * {"errcode":0,"errmsg":"ok"}
  495.          * {"errcode":49004,"errmsg":"not match signature"}
  496.          * {"errcode":49001,"errmsg":"not same appid with appid of access_token"}
  497.          */
  498.         public function deliverNotify(array $postData)
  499.         {
  500.                 // 检查参数
  501.                 $myPostData = array();
  502.                 foreach ($this->deliverNotifyParams as $key => $val) {
  503.                         if ($val['isRequired']) {
  504.                                 $myPostData[$key] = isset($postData[$key]) ? $postData[$key] : (isset($val['defaultValue']) ? $val['defaultValue'] : '');
  505.                                 if (!$myPostData[$key]) {
  506.                                         throw new WXPayException("Please specify value for $key", WXPayException::API_PARAM_ERROR);
  507.                                 }
  508.                         } else {
  509.                                 if (isset($postData[$key]) && $postData[$key]) {
  510.                                         $myPostData[$key] = $postData[$key];
  511.                                 }
  512.                         }
  513.                 }

  514.                 // 构造url
  515.                 $accessToken = WXBasic::factory()->getAccessToken();
  516.                 $url = WXConstant::PAY_DELIVER_NOTIFY_URL . "?access_token=$accessToken";
  517.                
  518.                 // 根据支付签名( paySign)生成方法中所讲的签名方式生成,
  519.                 $signMethod = $myPostData['sign_method'];
  520.                 unset($myPostData['sign_method']); // sign_method 是签名方法(不计入签名生成)
  521.                 $myPostData['appkey'] = isset($myPostData['appkey']) ? $myPostData['appkey'] : $this->paySignKey;
  522.                 $myPostData['app_signature'] = $this->createPaySign($myPostData);
  523.                 $myPostData['sign_method'] = $signMethod;
  524.                 unset($myPostData['appkey']); // appkey参加签名字段,但不需要传递
  525.                 $myPostData = json_encode($myPostData);
  526.                
  527.                 // 验证通知结果
  528.                 $res = WXBasic::factory()->sendPostRequest($url, $myPostData);
  529.                 if ($res['errcode'] != 0) {
  530.                         throw new WXPayException($res['errmsg'], WXPayException::DELIVER_NOTIFY_ERROR);
  531.                 }
  532.                
  533.             return $res;
  534.         }
  535.        
  536.         /**
  537.          * 查询订单orderquery
  538.          * 向微信查询订单信息
  539.          *
  540.          * @param array $postData
  541.          * array(
  542.          *         'out_trade_no' => ?,
  543.          * )
  544.          *
  545.          * 微信返回数据:
  546. {"errcode":49001,"errmsg":"not same appid with appid of access_token"}

  547. {"errcode":49004,"errmsg":"not match signature"}

  548. {"errcode":0,"errmsg":"ok","order_info":{
  549.         "ret_code":0,
  550.         "ret_msg":"",
  551.         "input_charset":"GBK",
  552.         "trade_state":"0",
  553.         "trade_mode":"1",
  554.         "partner":"1220730901",
  555.         "bank_type":"CMB_FP",
  556.         "bank_billno":"201409028971856",
  557.         "total_fee":"1",
  558.         "fee_type":"1",
  559.         "transaction_id":"1220730901201409023179351860",
  560.         "out_trade_no":"audia354058517b30b1",
  561.         "is_split":"false",
  562.         "is_refund":"false",
  563.         "attach":"",
  564.         "time_end":"20140902165147",
  565.         "transport_fee":"0",
  566.         "product_fee":"1",
  567.         "discount":"0",
  568.         "rmb_total_fee":""}
  569. }
  570.          */
  571.         public function orderQuery(array $postData)
  572.         {
  573.                 // 构造参数
  574.                 $myPostData['appid'] = isset($postData['appid']) ? $postData['appid'] : $this->appId;
  575.                 $myPostData['package'] = isset($postData['$package']) ? $postData['$package'] : '';
  576.                 $myPostData['timestamp'] = isset($postData['timestamp']) ? $postData['timestamp'] : time();
  577.                 $myPostData['app_signature'] = isset($postData['app_signature']) ? $postData['app_signature'] : '';
  578.                 $myPostData['sign_method'] = isset($postData['sign_method']) ? $postData['sign_method'] : 'sha1';
  579.                 if (!$myPostData['package']) {
  580.                         $outTradeNo = isset($postData['out_trade_no']) ? $postData['out_trade_no'] : '';
  581.                         $partner = isset($postData['partner']) ? $postData['partner'] : $this->partnerId;
  582.                         $sign = isset($postData['sign']) ? $postData['sign'] : '';
  583.                         if (!$outTradeNo) {
  584.                                 throw new WXPayException('please specify out_trade_no', WXPayException::API_PARAM_ERROR);
  585.                         }
  586.                         // 生成签名
  587.                         if (!$sign) {
  588.                                 $partnerKey = $this->partnerKey;
  589.                                 $sign = strtoupper(md5("out_trade_no=$outTradeNo&partner=$partner&key=$partnerKey"));
  590.                         }
  591.                         $myPostData['package'] = "out_trade_no=$outTradeNo&partner=$partner&sign=$sign";
  592.                 }
  593.                 // 生成签名
  594.                 if (!$myPostData['app_signature']) {
  595.                         $myPostData['app_signature'] = $this->createPaySign(array(
  596.                                         'appid' => $myPostData['appid'],
  597.                                         'appkey' => $this->paySignKey,
  598.                                         'package' => $myPostData['package'],
  599.                                         'timestamp' => $myPostData['timestamp'],
  600.                         ));
  601.                 }
  602.                
  603.                 $url = WXConstant::PAY_ORDER_QUERY_URL . "?access_token=" . WXBasic::factory()->getAccessToken();
  604.                 $res = WXBasic::factory()->sendPostRequest($url, json_encode($myPostData));
  605.                 if ($res['errcode'] != 0) {
  606.                         throw new WXPayException($res['errmsg'], WXPayException::ORDER_QUERY_ERROR);
  607.                 }
  608.                
  609.                 return $res;
  610.         }
  611.        
  612.         /**
  613.          * 接收来自微信的用户维权信息通知
  614.          * @param string $postXML
  615.          * @throws WXPayException
  616.          */
  617.         public function receivePayFeedback($postXML = '')
  618.         {
  619.                 if (!$postXML) {
  620.                         $postXML = file_get_contents('php://input');
  621.                 }
  622.                
  623.                 // 日志
  624.                 WXBasic::factory()->doLog($postXML, __METHOD__, 'mp.weixin.qq.com');
  625.                
  626.                 $postData = WXBasic::factory()->xml2Array($postXML);
  627.                
  628.                 // 校验AppSignature
  629.                 $myAppSignature = $this->createPaySign(array(
  630.                                 'appid' => $postData['appid'],
  631.                                 'appkey' => $this->paySignKey,
  632.                                 'timestamp' => $postData['timestamp'],
  633.                                 'openid' => $postData['openid'],
  634.                 ));
  635.                 if ($myAppSignature != $postData['AppSignature']) {
  636.                         throw new WXPayException('pay feedback sign validate failed:' . 'AppSignature=' . $postData['AppSignature'] . ';$myAppSignature='. $myAppSignature, WXPayException::PAY_FEEDBACK_SIGN_VALIDATE_FAILED);
  637.                 }
  638.                
  639.                 switch ($postData['MsgType']) {
  640.                         // 用户提交投诉
  641.                         case 'request':
  642.                                 break;
  643.                         // 用户确认消除投诉
  644.                         case 'confirm':
  645.                                 break;
  646.                         // 用户拒绝消除投诉
  647.                         case 'reject':
  648.                                 break;
  649.                         default:
  650.                                 break;
  651.                 }
  652.                
  653.                 // @todo 投诉单入库/更新投诉单状态
  654.                
  655.                 // 应答
  656.                 return 'ok';
  657.         }
  658.        
  659.         /**
  660.          * 标记客户的投诉处理状态
  661.          *
  662.          * 微信返回数据:
  663.          * {"errcode":0,"errmsg":"ok"}
  664.          * @param unknown $openId
  665.          * @param unknown $feedbackId
  666.          * @throws WXPayException
  667.          */
  668.         public function updatePayFeedback($openId, $feedbackId)
  669.         {
  670.                 $url = WXConstant::PAY_FEEDBACK_UPDATE_URL . "?access_token=" . WXBasic::factory()->getAccessToken()
  671.                                 . "&openid=$openId"
  672.                                 . "&feedbackid=$feedbackId";
  673.                 $res = WXBasic::factory()->sendGETRequest($url);
  674.                 if ($res['errcode'] != 0) {
  675.                         throw new WXPayException($res['errmsg'], WXPayException::PAY_FEEDBACK_UPDATE_ERROR);
  676.                 }
  677.                
  678.                 return $res;
  679.         }
  680.        
  681.         /**
  682.          * 接收告警信息
  683.          * @throws WXPayException
  684.          * @return string
  685.          */
  686.         public function receiveAlert($postXML)
  687.         {
  688.                 if (!$postXML) {
  689.                         $postXML = file_get_contents('php://input');
  690.                 }
  691.                
  692.                 // 日志
  693.                 WXBasic::factory()->doLog($postXML, __METHOD__, 'mp.weixin.qq.com');
  694.                
  695.                 $postData = WXBasic::factory()->xml2Array($postXML);
  696.                
  697.                 // 校验AppSignature
  698.                 $myAppSignature = $this->createPaySign(array(
  699.                                 'alarmcontent' => $postData['alarmcontent'],
  700.                                 'appid' => $postData['appid'],
  701.                                 'appkey' => $this->paySignKey,
  702.                                 'description' => $postData['description'],
  703.                                 'errortype' => $postData['errortype'],
  704.                                 'timestamp' => $postData['timestamp'],
  705.                 ));
  706.                 if ($myAppSignature != $postData['AppSignature']) {
  707.                         throw new WXPayException('pay feedback sign validate failed:' . 'AppSignature=' . $postData['AppSignature'] . ';$myAppSignature='. $myAppSignature, WXPayException::PAY_ALERT_SIGN_VALIDATE_FAILED);
  708.                 }
  709.                
  710.                 // @todo 告警通知入库
  711.                
  712.                 return 'success';
  713.         }
  714.        
  715.         /**
  716.          * 接收前端js的支付回调结果
  717.          * 注意这里没有签名机制,非100%可信
  718.          */
  719.         public function recieveJsCallback($openId, $outTradeNo)
  720.         {
  721.                 return Order::factory()->update(array('FTimeJSCallback' => date('Y-m-d H:i:s')), array('FOutTradeNo' => $outTradeNo, 'FOpenId' => $openId));
  722.         }
  723.        
  724. }
复制代码


wuchunuan 发表于 2015-10-15 10:21:15 | 显示全部楼层
沙发沙发
回复

使用道具 举报

eqmz 发表于 2015-10-15 10:21:37 | 显示全部楼层
此贴必火
回复

使用道具 举报

snkeyu 发表于 2015-10-15 10:23:56 | 显示全部楼层
支持!!!!!!
回复

使用道具 举报

风暴之怒 发表于 2015-10-15 10:40:12 | 显示全部楼层
支持,留着以后看
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|Discuz! 官方站 ( 皖ICP备16010102号 )star

GMT+8, 2024-5-5 15:33 , Processed in 0.111213 second(s), 16 queries , Gzip On.

Powered by Discuz! X3.4

Copyright © 2001-2023, Tencent Cloud.

快速回复 返回顶部 返回列表