123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386 |
- <?php
- /**
- * 微信支付
- * Created by PhpStorm.
- * User: phperstar
- * Date: 2019/11/28
- * Time: 2:12 PM
- */
- namespace Util\WeiXin;
- use Mall\Framework\Core\ErrorCode;
- Use Mall\Framework\Core\ResultWrapper;
- class Pay
- {
- /**
- * 小程序 appId
- * @var string $appid
- */
- private $appid;
- /**
- * 商户号
- * @var string $mch_id
- */
- private $mch_id;
- /**
- * 商户支付密钥
- */
- private $partnerKey;
- /**
- * 微信支付异步通知地址
- */
- private $notifyUrl = PAY_NOTIFY_URL.'/common/WeiXinPayNotify/notify';
- /**
- * 公共的接口请求地址
- * @var string
- */
- private $apiUrl = 'https://api.mch.weixin.qq.com/pay/';
- /**
- * 基础得请求地址
- */
- private $baseUrl = 'https://api.mch.weixin.qq.com/';
- /**
- * Pay constructor.
- * @param string $appid
- * @param $mch_id
- * @param $partnerKey
- */
- public function __construct($appid='', $mch_id, $partnerKey)
- {
- $this->appid = $appid;
- $this->mch_id = $mch_id;
- $this->partnerKey = $partnerKey;
- }
- /**
- * 统一下单接口
- */
- public function unifiedorder($orderNo, $total_fee, $ip, $source, $shopName, $openid = '', $attach = '')
- {
- $url = $this->apiUrl.'unifiedorder';
- switch ($source){
- case 'byteDanceH5':
- $trade_type = 'MWEB';
- break;
- case 'H5':
- $trade_type = 'MWEB';
- break;
- case 'APP':
- $trade_type = 'APP';
- break;
- default:
- $trade_type = 'JSAPI';
- break;
- }
- $params = [
- 'appid' => $this->appid, // 小程序ID/微信开放平台审核通过的应用APPID
- 'mch_id' => $this->mch_id, // 商户号
- 'nonce_str' => md5(md5(time().'qianniao.vip')), // 32位随机字符串
- 'out_trade_no' => $orderNo, // 商户订单号
- 'total_fee' => yuanToFen($total_fee), // 订单总金额,单位分
- 'spbill_create_ip' => $ip, // 终端ip
- 'trade_type' => $trade_type, // 交易类型
- 'notify_url' => $this->notifyUrl, // 通知地址
- 'body' => $shopName.'-'.'线上商城',
- 'attach' => $attach, // 附加数据
- ];
- if($trade_type == 'JSAPI'){
- $params['openid'] = $openid;
- }
- $params['sign'] = self::getSign($params);
- $post_xml = arrayToWeiXinXml($params);
- $curl_content = request($url, $post_xml);
- $result = $this->commonResult($curl_content);
- if(!$result->isSuccess()){
- return ResultWrapper::fail($result->getData(), $result->getErrorCode());
- }
- $response_content = $result->getData();
- //获取统一下单返回的id
- $prepay_id = $response_content['prepay_id'];
- // H5支付返回的支付跳转链接
- $mweb_url = isset($response_content['mweb_url']) ? $response_content['mweb_url'] : '';
- switch ($source){
- case 'byteDanceH5':
- $payParams = $mweb_url; // 字节跳动小程序调用微信H5支付获取支付链接
- break;
- case 'H5':
- $payParams = self::payment($prepay_id);
- break;
- case 'APP':
- $payParams = self::payment($prepay_id);
- break;
- default:
- $payParams = self::paymentToMinprogram($prepay_id);
- break;
- }
- return ResultWrapper::success($payParams);
- }
- /**
- * 小程序调起支付用到的参数
- * 官方接口地址: https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=7_7&index=3
- */
- public function paymentToMinprogram($prepay_id)
- {
- unset($params);
- $params = [
- 'appId' => $this->appid, // 小程序ID
- 'timeStamp' => (string)time(), //时间戳
- 'nonceStr' => md5(md5(time().'qianniao.vip')), //32位随机字符串
- 'package' => 'prepay_id='.$prepay_id, // 数据包
- 'signType' => 'MD5', // 签名方式
- ];
- $params['paySign'] = self::getSign($params);
- return $params;
- }
- /**
- * 调起支付用到的参数
- * 官方接口地址: https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_12
- * @param $prepay_id
- */
- public function payment($prepay_id)
- {
- unset($parm);
- $parm = array(
- 'appid' => $this->appid,
- 'partnerid' => $this->mch_id,
- 'timestamp' => (string)time(), //时间戳
- 'noncestr' => md5(md5(time().'qianniao.vip')), //32位随机字符串
- 'prepayid' => $prepay_id, //统一下单接口返回的prepay_id 参数值
- 'package' => 'Sign=WXPay',
- );
- $parm['sign'] = self::getSign($parm);
- return $parm;
- }
- /**
- * 付款码支付
- * 官方文档地址: https://pay.weixin.qq.com/wiki/doc/api/micropay.php?chapter=9_10&index=1
- */
- public function micropay($orderNo, $total_fee, $ip, $shopName, $auth_code)
- {
- $url = $this->apiUrl.'micropay';
- $params = [
- 'appid' => $this->appid, // 小程序ID/微信开放平台审核通过的应用APPID
- 'mch_id' => $this->mch_id, // 商户号
- 'nonce_str' => md5(md5(time().'qianniao.vip')), // 32位随机字符串
- 'body' => $shopName.'-'.'线上商城',
- 'out_trade_no' => $orderNo, // 商户订单号
- 'total_fee' => yuanToFen($total_fee), // 订单总金额,单位分
- 'spbill_create_ip' => $ip, // 终端ip
- 'auth_code' => $auth_code,
- ];
- $params['sign'] = self::getSign($params);
- $post_xml = arrayToWeiXinXml($params);
- $curl_content = request($url, $post_xml);
- if($curl_content['httpcode'] != '200'){
- return ResultWrapper::fail($curl_content['errorMsg'], ErrorCode::$apiNotResult);
- }
- $response_content = (array)simplexml_load_string($curl_content['content'], 'SimpleXMLElement', LIBXML_NOCDATA);
- if($response_content['return_code'] != 'SUCCESS'){
- return ResultWrapper::fail($response_content['return_msg'], ErrorCode::$weixinPayError);
- }
- if($response_content['result_code'] != 'SUCCESS' && $response_content['err_code'] != 'USERPAYING' ){
- return ResultWrapper::fail($response_content['err_code_des'], ErrorCode::$weixinPayError);
- }
- // 20s内查询支付状态,如果有明确错误直接返回错误,其他情况继续查询。如果成功直接返回成功
- for($i=1; $i<=10; $i++)
- {
- $url = $this->apiUrl.'orderquery';
- $params = [
- 'appid' => $this->appid, // 小程序ID/微信开放平台审核通过的应用APPID
- 'mch_id' => $this->mch_id, // 商户号
- 'nonce_str' => md5(md5(time().'qianniao.vip')), // 32位随机字符串
- 'out_trade_no' => $orderNo, // 商户订单号
- ];
- $params['sign'] = self::getSign($params);
- $post_xml = arrayToWeiXinXml($params);
- $curl_content = request($url, $post_xml);
- if($curl_content['httpcode'] != '200'){
- return ResultWrapper::fail($curl_content['errorMsg'], ErrorCode::$apiNotResult);
- }
- $response_content = (array)simplexml_load_string($curl_content['content'], 'SimpleXMLElement', LIBXML_NOCDATA);
- if($response_content['return_code'] != 'SUCCESS'){
- return ResultWrapper::fail($response_content['return_msg'], ErrorCode::$weixinPayError);
- }
- if($response_content['result_code'] != 'SUCCESS' && $response_content['err_code'] == 'ORDERNOTEXIST'){
- return ResultWrapper::fail($response_content['err_code_des'], ErrorCode::$weixinPayError);
- }
- if($response_content['return_code'] == 'SUCCESS' && $response_content['result_code'] == 'SUCCESS' && $response_content['trade_state'] == 'SUCCESS'){
- return ResultWrapper::success($response_content['trade_state']);
- }
- sleep(2);
- }
- // 20s内没有支付则撤销订单
- for($i=1; $i<=10; $i++){
- $url = $this->baseUrl.'secapi/pay/reverse';
- $params = [
- 'appid' => $this->appid, // 小程序ID/微信开放平台审核通过的应用APPID
- 'mch_id' => $this->mch_id, // 商户号
- 'nonce_str' => md5(md5(time().'qianniao.vip')), // 32位随机字符串
- 'out_trade_no' => $orderNo, // 商户订单号
- ];
- $params['sign'] = self::getSign($params);
- $post_xml = arrayToWeiXinXml($params);
- $curl_content = request($url, $post_xml, 10, false, [], true);
- if($curl_content['httpcode'] != '200'){
- return ResultWrapper::fail($curl_content['errorMsg'], ErrorCode::$apiNotResult);
- }
- $response_content = (array)simplexml_load_string($curl_content['content'], 'SimpleXMLElement', LIBXML_NOCDATA);
- if($response_content['return_code'] != 'SUCCESS'){
- return ResultWrapper::fail($response_content['return_msg'], ErrorCode::$weixinPayError);
- }
- if($response_content['result_code'] != 'SUCCESS' && $response_content['recall'] == 'N'){
- return ResultWrapper::fail($response_content['err_code_des'], ErrorCode::$weixinPayError);
- }
- if($response_content['return_code'] == 'SUCCESS' && $response_content['result_code'] == 'SUCCESS' && $response_content['recall'] == 'N'){
- return ResultWrapper::success($response_content['result_code']);
- }
- }
- }
- /**
- * H5查询支付状态接口
- */
- public function orderquery($orderNo)
- {
- $url = $this->apiUrl.'orderquery';
- $params = [
- 'appid' => $this->appid, // 小程序ID/微信开放平台审核通过的应用APPID
- 'mch_id' => $this->mch_id, // 商户号
- 'nonce_str' => md5(md5(time().'qianniao.vip')), // 32位随机字符串
- 'out_trade_no' => $orderNo, // 商户订单号
- ];
- $params['sign'] = self::getSign($params);
- $post_xml = arrayToWeiXinXml($params);
- $curl_content = request($url, $post_xml);
- $result = $this->commonResult($curl_content);
- if(!$result->isSuccess()){
- return ResultWrapper::fail($result->getData(), $result->getErrorCode());
- }
- $response_content = $result->getData();
- $trade_state = $response_content['trade_state'];
- return ResultWrapper::success($trade_state);
- }
- /**
- * 申请退款API
- * 官方文档地址:https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=9_4
- */
- public function transfers($outerTradeNo, $out_refund_no, $refundMoney, $orderMoney, $sslData)
- {
- $url = $this->baseUrl.'secapi/pay/refund';
- $params = [
- 'appid' => $this->appid,
- 'mch_id' => $this->mch_id,
- 'nonce_str' => md5(md5(time().'qianniao.vip')), // 32位随机字符串
- 'transaction_id' => $outerTradeNo, // 微信交易号
- 'out_refund_no' => $out_refund_no, // 商户退款单号
- 'refund_fee' => yuanToFen($refundMoney), // 退款金额
- 'total_fee' => yuanToFen($orderMoney), // 订单总金额
- ];
- $params['sign'] = self::getSign($params);
- $post_xml = arrayToWeiXinXml($params);
- $curl_content = request($url, $post_xml, 10, false, [], true, $sslData);
- $result = $this->commonResult($curl_content);
- if(!$result->isSuccess()){
- return ResultWrapper::fail($result->getData(), $result->getErrorCode());
- }
- $response_content = $result->getData();
- return ResultWrapper::success($response_content['refund_id']);
- }
- /**
- * 公共处理返回结果方法
- */
- public function commonResult($curl_content)
- {
- if($curl_content['httpcode'] == '200'){
- $response_content = (array)simplexml_load_string($curl_content['content'], 'SimpleXMLElement', LIBXML_NOCDATA);
- if($response_content['return_code'] == 'SUCCESS'){
- if($response_content['result_code'] == 'SUCCESS'){
- return ResultWrapper::success($response_content);
- }else{
- return ResultWrapper::fail($response_content['err_code_des'], ErrorCode::$weixinPayError);
- }
- }else{
- return ResultWrapper::fail($response_content['return_msg'], ErrorCode::$weixinPayError);
- }
- }else{
- return ResultWrapper::fail($curl_content['errorMsg'], ErrorCode::$apiNotResult);
- }
- }
- /**
- * 生成签名方法
- * 官方接口地址: https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=4_3
- * 官方签名测试地址: https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=20_1
- * @param $parm
- * @return string
- */
- public function getSign($parm)
- {
- //非空参数值的参数按照参数名ASCII码从小到大排序(字典序)
- if(ksort($parm)){
- $stringA = '';
- //使用URL键值对的格式拼接成字符串stringA
- foreach($parm as $key => $value){
- if($value == 0 || !empty($value)){
- $stringA .=$key.'='.$value.'&';
- }
- }
- }else{
- echo "对参数排序出错";
- exit();
- }
- //在stringA最后拼接上key=商户支付密钥
- $stringSignTemp = $stringA.'key='.$this->partnerKey;
- //对stringSignTemp进行MD5运算,再将得到的字符串所有字符转换为大写
- $signValue = strtoupper(md5($stringSignTemp));
- return $signValue;
- }
- }
|