* @date: 2019/1/18 */ namespace payment\wechat; /** * 文档地址: https://pay.weixin.qq.com/wiki/doc/api/index.html * 预下单方法: * $wechatPay = new WechatPay($config); * $info = $wechatPay->unifiedOrder($payData); * $info['return_code'] == 'FAIL' && json_error($info['return_msg'], -2); * $info['result_code'] == 'FAIL' && json_error($info['err_code_des'], -2); * * 1.扫码支付: $info['code_url'] 此url用于生成支付二维码,然后提供给用户进行扫码支付。注意:code_url的值并非固定,使用时按照URL格式转成二维码即可 * 2.H5支付: $info['mweb_url'] mweb_url为拉起微信支付收银台的中间页面,可通过访问该url来拉起微信客户端,完成支付,mweb_url的有效期为5分钟 * 可拼接跳转链接: $info['mweb_url'].'&redirect_url='.urlencode($returnUrl); * 3.App支付: $wechatPay->getAppParam($info['prepay_id']); 将结果返回给App端即可 * 4.Jsapi支付: $wechatPay->getJsapiParam($info['prepay_id']); 将结果返回给微信内H5支付或小程序端即可 * * Class WechatPay * @package app\common\extend\wechat */ class WechatPay extends BaseWechat { const UNIFIED_ORDER_URL = "/pay/unifiedorder"; //下单地址 const ORDER_QUERY_URL = "/pay/orderquery"; //查询订单 const CLOSE_ORDER_URL = "/pay/closeorder"; //关闭订单 public static $tradeMap = [ 'jsapi' => 'JSAPI', //小程序支付、公众号支付(微信内H5环境) 'app' => 'APP', //APP支付 'native' => 'NATIVE', //扫码支付 'mweb' => 'MWEB' //H5支付,非微信内H5环境 ]; private $openid; //公众号支付 用户openid private $wapUrl; //H5支付 WAP网站URL地址 private $wapName; //H5支付 WAP网站名 private $qrcodeTimeoutExpress; //扫码支付 二维码失效时间,单位:秒 public function __construct($config) { parent::__construct($config); } //下单参数检测 private function check($param) { isset($param['body']) || json_error('缺少参数:body'); isset($param['out_trade_no']) || json_error('缺少参数:out_trade_no'); isset($param['total_fee']) || json_error('缺少参数:total_fee'); isset($param['trade_type']) || json_error('缺少参数:trade_type'); isset($param['notify_url']) || json_error('缺少参数:notify_url'); //不同支付类型时 参数检测 if ($param['trade_type'] == self::$tradeMap['jsapi']) { //公众号支付 isset($param['openid']) || json_error('缺少参数:openid'); $this->openid = $param['openid']; } elseif ($param['trade_type'] == self::$tradeMap['native']) { //扫码支付 isset($param['expire']) || json_error('缺少参数:expire'); $this->qrcodeTimeoutExpress = $param['expire']; } elseif ($param['trade_type'] == self::$tradeMap['mweb']) { //H5支付 isset($param['wap_url']) || json_error('缺少参数:wap_url'); isset($param['wap_name']) || json_error('缺少参数:wap_name'); $this->wapUrl = $param['wap_url']; $this->wapName = $param['wap_name']; } } //下单方法 public function unifiedOrder($param) { $this->check($param); $data['appid'] = $this->appId; $data['mch_id'] = $this->mchId; $data['notify_url'] = $this->notifyUrl; $data['body'] = $param['body']; $data['out_trade_no'] = $param['out_trade_no']; $data['total_fee'] = $param['total_fee']; $data['trade_type'] = $param['trade_type']; $data['notify_url'] = $param['notify_url']; $data['nonce_str'] = $this->getRandomString(); $data['spbill_create_ip'] = get_client_ip(); $data['sign_type'] = $this->signType; //不同支付类型时 参数传入 if ($param['trade_type'] == self::$tradeMap['jsapi']) { $data['openid'] = $this->openid; } elseif ($param['trade_type'] == self::$tradeMap['native']) { $data['time_start'] = date("YmdHis"); $data['time_expire'] = date("YmdHis", time() + $this->qrcodeTimeoutExpress); } elseif ($param['trade_type'] == self::$tradeMap['mweb']) { $senseInfo = ['h5_info' => ['type' => 'Wap', 'wap_url' => $this->wapUrl, 'wap_name' => $this->wapName]]; $data['scene_info'] = json_encode($senseInfo); } //获取签名数据 $data['sign'] = $this->makeSign($data); $xml = $this->dataToXml($data); $response = $this->postXmlCurl($xml, self::UNIFIED_ORDER_URL); if (!$response) return false; $result = $this->xmlToArray($response); return $result; } //生成 APP端支付参数 public function getAppParam($prepayId) { $data['appid'] = $this->appId; $data['partnerid'] = $this->mchId; $data['prepayid'] = $prepayId; $data['package'] = 'Sign=WXPay'; $data['noncestr'] = $this->getRandomString(); $data['timestamp'] = time(); $data['sign_type'] = $this->signType; $data['sign'] = $this->makeSign($data); return $data; } //生成微信内H5支付、小程序端支付参数 注意参数大小写 public function getJsapiParam($prepayId) { $data['appId'] = $this->appId; $data['nonceStr'] = $this->getRandomString(); $data['package'] = 'prepay_id=' . $prepayId; $data['timeStamp'] = time(); $data['signType'] = $this->signType; $data['paySign'] = $this->makeSign($data); return $data; } //查询订单 public function queryOrder($outTradeNo) { $data['appid'] = $this->appId; $data['mch_id'] = $this->mchId; $data['nonce_str'] = $this->getRandomString(); $data['out_trade_no'] = $outTradeNo; $data['sign_type'] = $this->signType; $data['sign'] = $this->makeSign($data); $xml = $this->dataToXml($data); $response = $this->postXmlCurl($xml, self::ORDER_QUERY_URL); if (!$response) return false; $result = $this->xmlToArray($response); return $result; } //关闭订单 public function closeOrder($outTradeNo) { $data['appid'] = $this->appId; $data['mch_id'] = $this->mchId; $data['nonce_str'] = $this->getRandomString(); $data['out_trade_no'] = $outTradeNo; $data['sign_type'] = $this->signType; $data['sign'] = $this->makeSign($data); $xml = $this->dataToXml($data); $response = $this->postXmlCurl($xml, self::CLOSE_ORDER_URL); if (!$response) return false; $result = $this->xmlToArray($response); return $result; } //获取支付结果通知数据 public function getNotifyData() { //获取通知的数据 $xml = file_get_contents('php://input'); if (!$xml) return false; $data = $this->xmlToArray($xml); if (isset($data['return_code']) && $data['return_code'] == 'FAIL') return false; return $data; } //对后台通知交互时,如果微信收到商户的应答不是成功或超时,微信认为通知失败,微信会通过一定的策略定期重新发起通知,尽可能提高通知的成功率,但微信不保证通知最终能成功。 //通知频率为15/15/30/180/1800/1800/1800/1800/3600,单位:秒 public function replyNotify() { $data['return_code'] = 'SUCCESS'; $data['return_msg'] = 'OK'; $xml = $this->dataToXml($data); echo $xml; die(); } }