用户
 找回密码
 立即注册

QQ登录

只需一步,快速开始

扫一扫,登录网站

小程序社区 首页 教程 实战教程 查看内容

微信小程序支付 退款 订单查询 退款查询

Rolan 2020-11-16 10:57

微信小程序或微信支付相关操作

基于thinkphp5框架

微信小程序或微信支付相关操作

目录

支付

退款

订单查询

退款查询

支付成功,进行回调

退款成功 进行回调

用到的方法


支付
  1. /**
  2. * 预支付请求接口(POST)
  3. * @param string $openid openid
  4. * @param string $body 商品简单描述
  5. * @param string $order_sn 订单编号
  6. * @param string $total_fee 金额
  7. * @return json的数据
  8. */
  9. public function prepay()
  10. {
  11. tp_log('预支付请求数据===>' . json_encode($_POST), 'prepay', request()->controller());
  12. $goods_user = db('tf_goods_user')->where(array('order_no' => $_POST['order_no']))->find();
  13. $goods = db('tf_goods')->where(array('id' => $goods_user['goods_id']))->find();
  14. //判断产品的数量
  15. if (($goods['sales_initial'] - $goods['sales_actual']) <= 0) {
  16. $return['status'] = 0;
  17. $return['info'] = '此产品已售完';
  18. exit(json_encode($return));
  19. }
  20. $order_no = $_POST['order_no']; //订单号
  21. $is_sale = $_POST['is_sale'];
  22. $openid = $_POST['openid'];
  23. $goods_name = $_POST['goods_name'];
  24. $pay_price = $_POST['price'];
  25. $attach['is_sale'] = $_POST['is_sale'];
  26. $attach['sale_id'] = $_POST['sale_id'];
  27. $nonce_str = $this->nonce_str();//随机字符串
  28. $order_no_ssh = $this->get_orderssh(); //商户订单号
  29. //注意 订单号为自己创建的订单 和支付没有任何关系 商户订单号是支付单号 用来支付的 每当订单号发起支付 就会有新的商户订单号
  30. 商户订单号是不重复的 发起一次支付就要生成新的商户订单号 要保存下来 订单表要有对应的支付订单表 而订单号与商户订单号是一对多的关系
  31. 订单表最终保存的商户订单号是支付成功的那个 也可以 没调起支付就根据订单号去修改订单表的商户订单号 只有商户订单号才能发起退款
  32. 相当于商户订单号统计记录到订单表里面 退款查询等操作 商户订单号 其实订单生成就不会改数据了 也可以用订单号改为商户订单号 这样支付 取消 在支付 也可以的

  33. (out_trade_no)
  34. //组装支付数据
  35. $data = [
  36. 'appid' => config('pay.APPID'),
  37. 'mch_id' => config('pay.MCHID'),
  38. 'nonce_str' => $nonce_str,
  39. 'body' => $goods_name, //商品名称组合
  40. 'attach' => json_encode($attach),
  41. 'out_trade_no' => $order_no_ssh,//$order_no, //订单号 商户订单号
  42. 'total_fee' => intval($pay_price * 100),
  43. 'spbill_create_ip' => $_SERVER['REMOTE_ADDR'],
  44. 'notify_url' => config('pay.NOTIFY_URL'),
  45. 'trade_type' => 'JSAPI',
  46. 'openid' => $openid
  47. ];
  48. //订单支付表创建订单支付数据
  49. $p_o_data['createtime'] = time();
  50. $p_o_data['order_no'] = $order_no;
  51. $p_o_data['order_no_ssh'] = $order_no_ssh;
  52. $p_o_data['ready'] = json_encode($data);
  53. $p_o_return = db('tf_pay_order')->insert($p_o_data);
  54. if(!$p_o_return){
  55. //失败
  56. $return['status'] = -1;
  57. $return['info'] = $p_o_data;
  58. exit(json_encode($return));
  59. }
  60. // 获取签名
  61. $sign = $this->makeSign($data);
  62. $data['sign'] = $sign;
  63. $xml = $this->toXml($data);
  64. $url = 'https://api.mch.weixin.qq.com/pay/unifiedorder'; //发起支付接口链接
  65. //发起预支付请求
  66. $prepay_return_reslut_xml = $this->http_request($url, $xml);
  67. $xml_to_arr = $this->fromXml($prepay_return_reslut_xml);
  68. $return_result = json_encode($xml_to_arr, true);
  69. tp_log('预支付请求返回数据array===>' .$return_result , 'prepay', request()->controller());
  70. //记录预支付返回信息
  71. db('tf_goods_order')->where(array('order_no' => $order_no))
  72. ->update(array(
  73. 'go_pay' => $return_result,
  74. 'updatetime' => time(),
  75. 'updateuser' => $openid
  76. ));
  77. if($xml_to_arr['return_code'] == 'SUCCESS' && $xml_to_arr['result_code'] == 'SUCCESS'){
  78. //成功
  79. $time = time();
  80. //临时数组用于签名
  81. $tmp = [
  82. 'appId' => config('pay.APPID'),
  83. 'nonceStr' => $nonce_str,
  84. 'package' => 'prepay_id='.$xml_to_arr['prepay_id'],
  85. 'signType' => 'MD5',
  86. 'timeStamp' => "$time",
  87. ];
  88. $data['timeStamp'] = "$time";//时间戳
  89. $data['nonceStr'] = $nonce_str;//随机字符串
  90. $data['signType'] = 'MD5';//签名算法,暂支持 MD5
  91. $data['package'] = 'prepay_id='.$xml_to_arr['prepay_id'];//统一下单接口返回的 prepay_id 参数值,提交格式如:prepay_id=*
  92. $data['paySign'] = $this->makeSign($tmp);//签名,具体签名方案参见微信公众号支付帮助文档;$data['out_trade_no'] = $out_trade_no;
  93. $return['status'] = 1;
  94. $return['info'] = $data;
  95. }else{
  96. //失败
  97. $return['status'] = -1;
  98. $return['info'] = $xml_to_arr;
  99. }
  100. exit(json_encode($return));
  101. }
  102. //curl请求
  103. public function http_request($url, $data = null, $headers = array())
  104. {
  105. $curl = curl_init();
  106. if (count($headers) >= 1) {
  107. curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
  108. }
  109. curl_setopt($curl, CURLOPT_URL, $url);
  110. curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);
  111. curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE);
  112. if (!empty($data)) {
  113. curl_setopt($curl, CURLOPT_POST, 1);
  114. curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
  115. }
  116. curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
  117. $output = curl_exec($curl);
  118. curl_close($curl);
  119. return $output;
  120. }
退款
  1. /**
  2. * 申请退款API
  3. * @param $transaction_id
  4. * @param double $total_fee 账单总金额
  5. * @param double $refund_fee 退款金额
  6. * @return bool
  7. * @throws BaseException
  8. */
  9. public function refund()
  10. {
  11. $transaction_id = '4200000712202007274705432240';
  12. $total_fee = '0.01';
  13. $refund_fee = '0.01';
  14. // 当前时间
  15. $time = time();
  16. // 生成随机字符串
  17. $nonceStr = md5($time . $transaction_id . $total_fee . $refund_fee);
  18. // API参数
  19. $params = [
  20. 'appid' => config('pay.APPID'),
  21. 'mch_id' => config('pay.MCHID'),
  22. 'nonce_str' => $nonceStr,
  23. 'transaction_id' => $transaction_id,
  24. 'out_refund_no' => $time,
  25. 'total_fee' => $total_fee * 100,
  26. 'refund_fee' => $refund_fee * 100,
  27. 'notify_url' => config('pay.NOTIFY_URL_REFUND'),//退款回调地址
  28. ];
  29. // 生成签名
  30. $params['sign'] = $this->makeSign($params);
  31. tp_log('退款请求数据===>' . json_encode($params), 'refund', request()->controller());
  32. // 请求API
  33. $url = 'https://api.mch.weixin.qq.com/secapi/pay/refund';
  34. $result = $this->post($url, $this->toXml($params), true, $this->getCertPem());
  35. $prepay = $this->fromXml($result);
  36. // 请求失败
  37. if (empty($result)) {
  38. throw new BaseException(['msg' => '微信退款api请求失败']);
  39. }
  40. // 格式化返回结果
  41. $prepay = $this->fromXml($result);
  42. tp_log('退款返回数据===>' . json_encode($prepay), 'refund', request()->controller());
  43. // 请求失败
  44. // if ($prepay['return_code'] === 'FAIL') {
  45. // throw new BaseException(['msg' => 'return_msg: ' . $prepay['return_msg']]);
  46. // }
  47. // if ($prepay['result_code'] === 'FAIL') {
  48. // throw new BaseException(['msg' => 'err_code_des: ' . $prepay['err_code_des']]);
  49. // }
  50. return true;
  51. }
  52. /**
  53. * 模拟POST请求
  54. * @param $url
  55. * @param array $data
  56. * @param bool $useCert
  57. * @param array $sslCert
  58. * @return mixed
  59. */
  60. public function post($url, $data = [], $useCert = false, $sslCert = [])
  61. {
  62. $header = [
  63. 'Content-type: application/json;'
  64. ];
  65. $curl = curl_init();
  66. //如果有配置代理这里就设置代理
  67. // if(WxPayConfig::CURL_PROXY_HOST != "0.0.0.0"
  68. // && WxPayConfig::CURL_PROXY_PORT != 0){
  69. // curl_setopt($ch,CURLOPT_PROXY, WxPayConfig::CURL_PROXY_HOST);
  70. // curl_setopt($ch,CURLOPT_PROXYPORT, WxPayConfig::CURL_PROXY_PORT);
  71. // }
  72. curl_setopt($curl, CURLOPT_URL, $url);
  73. curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
  74. curl_setopt($curl, CURLOPT_HEADER, false);
  75. curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
  76. curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
  77. curl_setopt($curl, CURLOPT_POST, TRUE);
  78. curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
  79. if ($useCert == true) {
  80. // 设置证书:cert 与 key 分别属于两个.pem文件
  81. curl_setopt($curl, CURLOPT_SSLCERTTYPE, 'PEM');
  82. curl_setopt($curl, CURLOPT_SSLCERT, $sslCert['certPem']);
  83. curl_setopt($curl, CURLOPT_SSLKEYTYPE, 'PEM');
  84. curl_setopt($curl, CURLOPT_SSLKEY, $sslCert['keyPem']);
  85. }
  86. $result = curl_exec($curl);
  87. curl_close($curl);
  88. return $result;
  89. }
订单查询
  1. /**
  2. * 订单查询
  3. * @param $out_trade_no
  4. * @return mixed
  5. * @throws BaseException
  6. */
  7. public function orderquery()
  8. {
  9. $transaction_id = '4200000712202007274705432240';//微信订单号
  10. // 当前时间
  11. $time = time();
  12. // 生成随机字符串
  13. $nonce_str = md5($time . mt_rand(00000,99999));
  14. //API参数
  15. $params = [
  16. 'appid' => config('pay.APPID'), //公众号ID
  17. 'mch_id' => config('pay.MCHID'), //商户号
  18. 'transaction_id' => $transaction_id, //商户订单号
  19. 'nonce_str' => $nonce_str, // 随机字符串
  20. ];
  21. //生成签名
  22. $params['sign'] = $this->makeSign($params);
  23. //请求API
  24. $url = 'https://api.mch.weixin.qq.com/pay/orderquery';
  25. $result = $this->post($url, $this->toXml($params));
  26. $prepay = $this->fromXml($result);
  27. // 请求失败
  28. if ($prepay['return_code'] === 'FAIL') {
  29. throw new BaseException(['msg' => $prepay['return_msg'], 'code' => 0]);
  30. }
  31. if ($prepay['result_code'] === 'FAIL') {
  32. throw new BaseException(['msg' => $prepay['err_code_des'], 'code' => 0]);
  33. }
  34. return $prepay;
  35. }
退款查询
  1. /**
  2. * 退款查询
  3. * @param $out_trade_no
  4. * @return mixed
  5. * @throws BaseException
  6. */
  7. public function refundquery()
  8. {
  9. $transaction_id = '4200000712202007274705432240';//微信订单号
  10. // 当前时间
  11. $time = time();
  12. // 生成随机字符串
  13. $nonce_str = md5($time . mt_rand(00000,99999));
  14. //API参数
  15. $params = [
  16. 'appid' => config('pay.APPID'), //公众号ID
  17. 'mch_id' => config('pay.MCHID'), //商户号
  18. 'transaction_id' => $transaction_id, //商户订单号
  19. 'nonce_str' => $nonce_str, // 随机字符串
  20. ];
  21. //生成签名
  22. $params['sign'] = $this->makeSign($params);
  23. //请求API
  24. $url = 'https://api.mch.weixin.qq.com/pay/refundquery';
  25. $result = $this->post($url, $this->toXml($params));
  26. $prepay = $this->fromXml($result);
  27. dump($prepay);die;
  28. // 请求失败
  29. if ($prepay['return_code'] === 'FAIL') {
  30. throw new BaseException(['msg' => $prepay['return_msg'], 'code' => 0]);
  31. }
  32. if ($prepay['result_code'] === 'FAIL') {
  33. throw new BaseException(['msg' => $prepay['err_code_des'], 'code' => 0]);
  34. }
  35. return $prepay;
  36. }
支付成功,进行回调
  1. public function index()
  2. {
  3. $data = file_get_contents('php://input');
  4. $data = $this->FromXml($data);
  5. tp_log('支付回调数据===>' . json_encode($data), 'index', request()->controller());
  6. // 保存微信服务器返回的签名sign
  7. $data_sign = $data['sign'];
  8. // sign不参与签名算法
  9. unset($data['sign']);
  10. $sign = $this->makeSign($data);//回调验证签名
  11. $str_success = '<xml><return_code>return_code><return_msg>return_msg>xml>';
  12. $str_error = '<xml><return_code>return_code><return_msg>return_msg>xml>';
  13. if (($sign === $data_sign) && ($data['return_code'] == 'SUCCESS') && ($data['result_code'] == 'SUCCESS')) {
  14. // 支付成功 进行你的逻辑处理
  15. }
  16. echo $str_success;//str_error 告知微信 你已的逻辑处理完毕 不用再推送或再次推送你结果
  17. }
退款成功 进行回调
  1. /*
  2. * 小程序 退款结果通知
  3. */
  4. public function refund(){
  5. $data = file_get_contents('php://input');
  6. $data = $this->FromXml($data);
  7. tp_log('退款回调数据===>' . json_encode($data), 'refund', request()->controller());
  8. //对加密的字符串解密
  9. $req_info_xml = openssl_decrypt(base64_decode($data['req_info']), 'aes-256-ecb', md5(config('pay.KEY')),OPENSSL_RAW_DATA);
  10. $req_info = $this->FromXml($req_info_xml);
  11. // // 保存微信服务器返回的签名sign
  12. // $data_sign = $data['sign'];
  13. // // sign不参与签名算法
  14. // unset($data['sign']);
  15. // $sign = $this->makeSign($data);//回调验证签名
  16. //
  17. // $str_success = '<xml><return_code>return_code><return_msg>return_msg>xml>';
  18. // $str_error = '<xml><return_code>return_code><return_msg>return_msg>xml>';
  19. //
  20. //
  21. //
  22. // if (($sign === $data_sign) && ($data['return_code'] == 'SUCCESS') && ($data['result_code'] == 'SUCCESS')) {
  23. // tp_log('退款成功===>', 'refund', request()->controller());
  24. // //去修改订单的状态 和支付回调的一样 修改成功 告知微信 不在推送
  25. // }
  26. }
用到的方法
  1. /**
  2. * 生成签名
  3. * @param $values
  4. * @return string 本函数不覆盖sign成员变量,如要设置签名需要调用SetSign方法赋值
  5. */
  6. private function makeSign($values)
  7. {
  8. //签名步骤一:按字典序排序参数
  9. ksort($values);
  10. $string = $this->toUrlParams($values);
  11. //签名步骤二:在string后加入KEY
  12. $string = $string . '&key=' . config('pay.KEY');
  13. //签名步骤三:MD5加密
  14. $string = md5($string);
  15. //签名步骤四:所有字符转为大写
  16. $result = strtoupper($string);
  17. return $result;
  18. }
  19. private function ToUrlParams($array)
  20. {
  21. $buff = "";
  22. foreach ($array as $k => $v) {
  23. if ($k != "sign" && $v != "" && !is_array($v)) {
  24. $buff .= $k . "=" . $v . "&";
  25. }
  26. }
  27. $buff = trim($buff, "&");
  28. return $buff;
  29. }
  30. /**
  31. * 输出xml字符
  32. * @param $values
  33. * @return bool|string
  34. */
  35. private function toXml($values)
  36. {
  37. if (!is_array($values)
  38. || count($values) <= 0
  39. ) {
  40. return false;
  41. }
  42. $xml = "";
  43. foreach ($values as $key => $val) {
  44. if (is_numeric($val)) {
  45. $xml .= "<" . $key . ">" . $val . " . $key . ">";
  46. } else {
  47. $xml .= "<" . $key . "> . $val . "]]> . $key . ">";
  48. }
  49. }
  50. $xml .= "";
  51. return $xml;
  52. }
  53. /**
  54. * 将xml转为array
  55. * @param $xml
  56. * @return mixed
  57. */
  58. private function fromXml($xml)
  59. {
  60. // 禁止引用外部xml实体
  61. libxml_disable_entity_loader(true);
  62. return json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
  63. }
  64. /**
  65. * 获取cert证书文件
  66. * @return array
  67. * @throws BaseException
  68. */
  69. private function getCertPem()
  70. {
  71. // if (empty($this->config['cert_pem']) || empty($this->config['key_pem'])) {
  72. // throw new BaseException(['msg' => '请先到后台小程序设置填写微信支付证书文件']);
  73. // }
  74. // cert目录
  75. $filePath = EXTEND_PATH.'wxpay/cert/';
  76. return [
  77. 'certPem' => $filePath . 'apiclient_cert.pem',
  78. 'keyPem' => $filePath . 'apiclient_key.pem'
  79. ];
  80. }
  81. /**
  82. * 生成商户订单号
  83. */
  84. public function get_orderssh()
  85. {
  86. return date("YmdHis") . mt_rand(10000000, 99999999);
  87. }

证书路径:

在这里插入图片描述

config配置

在这里插入图片描述

 

在这里插入图片描述

鲜花
鲜花
鸡蛋
鸡蛋
分享至 : QQ空间
收藏