经验分享为热心网友提供,用于有能力的用户学习使用,不保证源码存在bug等问题

阿帕云Zkeys自定义支付说明:https://www.apayun.com/doc/614.html
分享者QQ:2448066128(代码主要部分);1005265250(手机端支付部分)

整理说明:根据阿帕云官网的自定义支付说明开发的,以下分享的是支付宝的支付方式,我只测试过PC端的支付,手机端的没有对接。(我已经把支付方式参数对接到配置中了,其实也可以理解是加了一种)。

备注: 支付方式的图标放在 public/template/user/Zkeys/PC/static/css/ucModule/finance/img/ 下面,图标名字为支付方式名和创建的对应php文件类名一样,也就是上传个ZhifufmAlipay.png 就可以了。

其他支付方式增加类似,举例增加微信支付,文件复制后修改如下地方:

  1. 文件名修改。发起部分如:ZhifufmAlipay.class.php 修改为 ZhifufmWepay.class.php;回调部分文件名字如:ZhifufmAlipayNotifyController.class.php 修改为 ZhifufmWepayNotifyController.class.php
  2. 类名修改。如:ZhifufmAlipay.class.php文件内:class ZhifufmAlipay implements Ipay 修改为 class ZhifufmWepay implements Ipay;ZhifufmAlipayNotifyController.class.php文件内:class ZhifufmAlipayNotifyController extends PaymentNotifyController 修改为 class ZhifufmWepayNotifyController extends PaymentNotifyController
  3. 发起部分的回调路径修改。发起部分如:ZhifufmAlipay.class.php 中/ApiNotify/ZhifufmAlipayNotify/notify 修改为 /ApiNotify/ZhifufmWepayNotify/notify
  4. 根据自己喜好上次支付方式的图标。如:ZhifufmWepayNotify.png

后台以及支付发起部分

在libs/Niaoyun/Payment 目录下新建要接入的支付方式ZhifufmAlipay.class.php文件,这个文件增加后,后台刷新应该就有配置入口了。代码内容如下:

  1. <?php
  2. namespace Niaoyun\Payment;
  3. use Niaoyun\Payment\PayInterface\Ipay;
  4. class ZhifufmAlipay implements Ipay{
  5. private $payment_method;
  6. private $config=[
  7. 'zhifu_merchant'=>'',
  8. 'zhifu_key' => '',
  9. 'zhifu_apiurl' => '',
  10. 'zhifu_paytype' => ''
  11. ];
  12. private $configAdmin=[
  13. 'zhifu_merchant' =>['name'=>'商户号','show'=>true],
  14. 'zhifu_key' =>['name'=>'接入密钥','show'=>true],
  15. 'zhifu_apiurl' =>['name'=>'接口地址','show'=>true],
  16. 'zhifu_paytype' =>['name'=>'支付方式传值','show'=>true]
  17. ];
  18. public function __construct(){
  19. foreach ($this->configAdmin as $k => $v){
  20. $this->config[$k] = C('recharge')[substr(__CLASS__, strrpos(__CLASS__,'\\') + 1).$k];
  21. }
  22. }
  23. public function getPayName(){
  24. return '支付FM';
  25. }
  26. //获取接口配置项,并展示与【平台后台】-【系统】-【全局设置】-【充值】中
  27. public function getConfigAdmin(){
  28. return $this->configAdmin;
  29. }
  30. public function getConfig(){
  31. return $this->config;
  32. }
  33. public function pay($params){
  34. $protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' || $_SERVER['SERVER_PORT'] == 443) ? "https://" : "http://";
  35. //$params array(3) { ["total_fee"]=> string(1) "1" ["out_trade_no"]=> string(18) "800000008" ["body"]=> string(39) "充值款,会员ID:11111" }
  36. $config = $this->config;
  37. // php form表单page方式自动跳转
  38. // 开发手册:http://docs.nephalem.cn/read/zhifufm/step
  39. $amount = $params ['total_fee']; // 获取充值金额
  40. $orderNo = $params['out_trade_no']; // 自己创建的本地订单号
  41. $merchantNum = $config['zhifu_merchant']; // 商户号, 商户后台的用户中心页面查看
  42. $secret = $config['zhifu_key']; // 商户密钥, 商户后台的用户中心页面查看
  43. $api_url = $config['zhifu_apiurl']; // 付款请求接口, 商户后台的用户中心页面查看
  44. $payType = $config['zhifu_paytype'];//'alipay';修改为前端传入的支付方式值,这样就可以自己控制了或者随时调整支付方式,查看支付接口文档说明payType的取值
  45. $notifyUrl = $protocol.$_SERVER['HTTP_HOST'].'/ApiNotify/ZhifufmAlipayNotify/notify'; // XXXX修改为您自己用来接收支付成功的公网地址
  46. $returnUrl = $protocol.$_SERVER['HTTP_HOST'].'/user/payment/record.html'; # 支付成功您想让页面跳转的地址
  47. $returnType = "json"; // 接口返回方式 page为直接跳转到支付页面,不传返回json
  48. $sign = md5 ( join ( '', array (
  49. $merchantNum,
  50. $orderNo,
  51. $amount,
  52. $notifyUrl,
  53. $secret
  54. ) ));
  55. $native = array (
  56. "merchantNum" => $merchantNum,
  57. "payType" => $payType,
  58. "amount" => $amount,
  59. "orderNo" => $orderNo,
  60. "notifyUrl" => $notifyUrl,
  61. "returnUrl" => $returnUrl,
  62. "sign" => $sign,
  63. "returnType" => $returnType
  64. );
  65. $param = http_build_query ( $native );
  66. $return = $this->http_request ( $api_url, $param, 'application/x-www-form-urlencoded;charset=utf-8' );
  67. if (strpos ( $return, '{' ) === 0) {
  68. $return = json_decode ( $return, true );
  69. if ($return ['success']) {
  70. // json方式展示支付链接有如下几种
  71. return [
  72. 'code'=>200, //200表示成功,201 表示失败
  73. 'html_text'=>$return ['data'] ['payUrl'], //返回的是跳转链接则用html_text字段,用于新窗口打开的页面
  74. 'msg'=>'success' //成功success,失败fail
  75. ];
  76. } else {
  77. return [
  78. 'code'=>201, //200表示成功,201 表示失败
  79. 'html_text'=>'', //返回的是跳转链接则用html_text字段,用于新窗口打开的页面
  80. 'msg'=>$return ['msg'] //成功success,失败fail
  81. ];
  82. exit($return ['msg']);
  83. }
  84. } else {
  85. return [
  86. 'code'=>201, //200表示成功,201 表示失败
  87. 'html_text'=>'', //返回的是跳转链接则用html_text字段,用于新窗口打开的页面
  88. 'msg'=>'请求异常' //成功success,失败fail
  89. ];
  90. exit( "请求异常");
  91. }
  92. // $html = '<html>
  93. // <head><title>redirect...</title></head>
  94. // <body>
  95. // <form id="post_data" action="' . $api_url . '" method="post">
  96. // <input type="hidden" name="merchantNum" value="' . $merchantNum . '"/>
  97. // <input type="hidden" name="payType" value="' . $payType . '"/>
  98. // <input type="hidden" name="amount" value="' . $amount . '"/>
  99. // <input type="hidden" name="orderNo" value="' . $orderNo . '"/>
  100. // <input type="hidden" name="notifyUrl" value="' . $notifyUrl . '"/>
  101. // <input type="hidden" name="returnUrl" value="' . $returnUrl . '"/>
  102. // <input type="hidden" name="sign" value="' . $sign . '"/>
  103. // <input type="hidden" name="returnType" value="' . $returnType . '"/>
  104. // </form>
  105. // <script>document.getElementById("post_data").submit();</script>
  106. // </body>
  107. // </html>';
  108. // return [
  109. // 'code'=>200, //200表示成功,400 表示失败
  110. // 'type'=> 'html', //固定类型,表示返回的是html页面
  111. // 'html'=>$html,//支付方式返回的是一个新的html自动跳转页面
  112. // 'msg'=>'success' //成功success,失败fail
  113. // ];
  114. }
  115. public function wapPay($params){
  116. $protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' || $_SERVER['SERVER_PORT'] == 443) ? "https://" : "http://";
  117. //$params array(3) { ["total_fee"]=> string(1) "1" ["out_trade_no"]=> string(18) "800000008" ["body"]=> string(39) "充值款,会员ID:11111" }
  118. $config = $this->config;
  119. // php form表单page方式自动跳转
  120. // 开发手册:http://docs.nephalem.cn/read/zhifufm/step
  121. $amount = $params ['total_fee']; // 获取充值金额
  122. $orderNo = $params['out_trade_no']; // 自己创建的本地订单号
  123. $merchantNum = $config['zhifu_merchant']; // 商户号, 商户后台的用户中心页面查看
  124. $secret = $config['zhifu_key']; // 商户密钥, 商户后台的用户中心页面查看
  125. $api_url = $config['zhifu_apiurl']; // 付款请求接口, 商户后台的用户中心页面查看
  126. $payType = $config['zhifu_paytype'];//'alipay';修改为前端传入的支付方式值,这样就可以自己控制了或者随时调整支付方式,查看支付接口文档说明payType的取值
  127. $notifyUrl = $protocol.$_SERVER['HTTP_HOST'].'/ApiNotify/ZhifufmAlipayNotify/notify'; // XXXX修改为您自己用来接收支付成功的公网地址
  128. $returnUrl = $protocol.$_SERVER['HTTP_HOST'].'/user/payment/record.html'; # 支付成功您想让页面跳转的地址
  129. $returnType = "json"; // 接口返回方式 page为直接跳转到支付页面,不传返回json
  130. $sign = md5 ( join ( '', array (
  131. $merchantNum,
  132. $orderNo,
  133. $amount,
  134. $notifyUrl,
  135. $secret
  136. ) ));
  137. $native = array (
  138. "merchantNum" => $merchantNum,
  139. "payType" => $payType,
  140. "amount" => $amount,
  141. "orderNo" => $orderNo,
  142. "notifyUrl" => $notifyUrl,
  143. "returnUrl" => $returnUrl,
  144. "sign" => $sign,
  145. "returnType" => $returnType
  146. );
  147. $param = http_build_query ( $native );
  148. $return = $this->http_request ( $api_url, $param, 'application/x-www-form-urlencoded;charset=utf-8' );
  149. if (strpos ( $return, '{' ) === 0) {
  150. $return = json_decode ( $return, true );
  151. if ($return ['success']) {
  152. // json方式展示支付链接有如下几种
  153. return [
  154. 'code'=>200, //200表示成功,400 表示失败
  155. 'type'=>'redirect',
  156. 'url'=> $return ['data'] ['payUrl'], //返回的是即将跳转的RUL地址
  157. 'msg'=>'success' //成功success,失败fail
  158. ];
  159. } else {
  160. return [
  161. 'code'=>201, //200表示成功,201 表示失败
  162. 'msg'=>$return ['msg'] //成功success,失败fail
  163. ];
  164. exit($return ['msg']);
  165. }
  166. } else {
  167. return [
  168. 'code'=>201, //200表示成功,201 表示失败
  169. 'msg'=>'请求异常' //成功success,失败fail
  170. ];
  171. exit( "请求异常");
  172. }
  173. }
  174. // 发送请求
  175. function http_request($url, $post_data = array(), $header = 'Content-Type: application/json') {
  176. $ch = curl_init ();
  177. curl_setopt ( $ch, CURLOPT_URL, $url );
  178. curl_setopt ( $ch, CURLOPT_RETURNTRANSFER, 1 );
  179. // 返回最后的Location
  180. curl_setopt ( $ch, CURLOPT_FOLLOWLOCATION, 1 );
  181. curl_setopt ( $ch, CURLOPT_POST, 1 );
  182. curl_setopt ( $ch, CURLOPT_POSTFIELDS, $post_data );
  183. curl_setopt ( $ch, CURLOPT_CONNECTTIMEOUT, 60 );
  184. curl_setopt ( $ch, CURLOPT_SSL_VERIFYPEER, FALSE );
  185. curl_setopt ( $ch, CURLOPT_SSL_VERIFYHOST, FALSE );
  186. curl_setopt ( $ch, CURLOPT_HTTPHEADER, array (
  187. $header,
  188. 'Content-Length: ' . strlen ( $post_data )
  189. ) );
  190. $contents = curl_exec ( $ch );
  191. curl_close ( $ch );
  192. return $contents;
  193. }
  194. }
  195. ?>

支付回调

在 app\ApiNotify\Controller\ 下创建回调php文件ZhifufmAlipayNotifyController.class.php 代码如下:

  1. <?php
  2. namespace ApiNotify\Controller;
  3. class ZhifufmAlipayNotifyController extends PaymentNotifyController{
  4. private $payConfig = [];
  5. function _initialize(){
  6. parent::_initialize();
  7. $className = substr(__CLASS__, strrpos(__CLASS__,'\\')+1,-16);
  8. $class = new \ReflectionClass("\Niaoyun\Payment\\$className");
  9. $obj = $class->newInstance();
  10. $this->payConfig = $obj->getConfig();
  11. }
  12. //验证签名
  13. public function checkSign($params){
  14. // $this->write_log("checkSign");
  15. // $this->write_log($params);
  16. $data_arr = array (
  17. $params ['state'],
  18. $this->payConfig['zhifu_merchant'],
  19. $params ['orderNo'],
  20. $params ['amount'],
  21. $this->payConfig['zhifu_key'],
  22. ) ;
  23. $sign = md5 ( join ( '', $data_arr ) );
  24. if($sign==$params['sign']){
  25. echo "success";
  26. return true;
  27. }else{
  28. return false;
  29. }
  30. }
  31. //回调状态
  32. public function getPayStatus($params){
  33. //$this->write_log($params);
  34. if($params['state']=='1'){
  35. $this->orderNo = $params['orderNo'];
  36. $this->trade_no = $params['platformOrderNo'];
  37. $this->total_fee = $params['amount'];
  38. // $this->payAccount = $params['mh_oid'];
  39. echo "success";
  40. return true;
  41. }else{
  42. return false;
  43. }
  44. }
  45. /**
  46. * write_log 写入日志,调试或者记录用,上线后可以删除也可以按需保留部分
  47. * Class中调用方式 $this->write_log(...)
  48. * @param [type] $data [写入的数据]
  49. * @return [type] [description]
  50. */
  51. function write_log($data) {
  52. $years = date ( 'Y-m' );
  53. // 设置路径目录信息
  54. $url = './zhifufm/' . $years . '/' . date ( 'Ymd' ) . '_request_log.log';
  55. $dir_name = dirname ( $url );
  56. // 目录不存在就创建
  57. if (! file_exists ( $dir_name )) {
  58. // iconv防止中文名乱码
  59. $res = mkdir ( iconv ( "UTF-8", "GBK", $dir_name ), 0777, true );
  60. }
  61. $fp = fopen ( $url, "a" ); // 打开文件资源通道 不存在则自动创建
  62. fwrite ( $fp, date ( "[Y-m-d H:i:s] " ) . var_export ( $data, true ) . "\r\n" ); // 写入文件
  63. fclose ( $fp ); // 关闭资源通道
  64. }
  65. }
  66. ?>