介绍一个支付组件riverslei/payment及本地如何进行支付宝支付测试

payment是一款集成了阿里支付、微信支付的组件。它对php的版本要求很低,大于等于5.6。
在这里插入图片描述
但是个人认为其文档做的不很完善,特别是异步通知并没有文档,所以有必要将一些细节分享给大家。下面介绍的是laravel5.1框架如何使用(什么框架并不重要),并重点介绍阿里支付的PC网页支付,因支付宝有沙箱测试,微信我未测试,故省略。

引入组件

composer require "riverslei/payment:*"

配置文件

在项目的配置目录下,新建payment.php,内容如下:

 [
        'name'            => '支付宝支付',
        'use_sandbox'     => true, //是否沙箱模式
        'partner'         => '2088********1126', //收款支付宝用户ID(2088开头)
        'app_id'          => '2016********9779',
        'sign_type'       => 'RSA2',
        'ali_public_key'  => '', //支付宝公钥
        'rsa_private_key' => storage_path('security/alipay/private_key.txt'),
        'limit_pay'       => [],
        'notify_url'      => env('APP_URL').'/usradmin/notify/ali_charge', //异步回调地址
        'return_url'      => env('APP_URL').'/usradmin/payres',  //同步回调地址
        'return_raw'      => false, //是否返回原始数据
    ],

    'wx_charge'  => [
        'name'         => '微信支付',
        //微信支付配置数组
        'app_id'       => '',
        'mch_id'       => '',
        'md5_key'      => '',
        'app_cert_pem' => storage_path('security/wechat/apiclient_cert.pem'),
        'app_key_pem'  => storage_path('security/wechat/apiclient_key.pem'),
        'sign_type'    => 'MD5',// MD5  HMAC-SHA256
        'limit_pay'    => [ ],
        'fee_type'     => 'CNY',// 货币类型  当前仅支持该字段
        'notify_url'   => env('APP_URL') . '/usradmin/notify/wx_charge',
        'return_url'      => env('APP_URL').'/usradmin/payres',  //同步回调地址
        'return_raw'   => false,
    ]
];

说明:

  • ‘rsa_private_key’ => storage_path(‘security/alipay/private_key.txt’),
    私钥建议保存在一个目录的txt文件中,不建议在这里直接暴露。当你用支付宝的密钥工具
    介绍一个支付组件riverslei/payment及本地如何进行支付宝支付测试_第1张图片
    放进一个目录(不能是public目录),可以是resorce/storge都可以。修改下文件名就行。
    介绍一个支付组件riverslei/payment及本地如何进行支付宝支付测试_第2张图片

充值

在控制器建立充值方法,调用Charge::run方法,返回发起支付的url,header函数跳转到该url即可。

	use Payment\Config;
	use Payment\Client\Charge;

   public function recharge(Request $request)
   {
       $money   = intval($request->input('pay_amount', 0)); //金额
       $pay_way = $request->input('pay_way', ''); //支付方式
       /**
        * ali_web:支付宝 PC 网页支付
        * wx_qr:微信 扫码支付  (可以使用app的帐号,也可以用公众的帐号完成)
        * 如果这里不清楚,去Payment\Config类查看,都在这里。
        */
       if (!in_array($pay_way, ['ali_web', 'wx_qr'])) {
           return Y::json(1001, '不支持当前支付方式');
       }

       //生成订单
       $payData = [
           'body'            => 'test', //对一笔交易的具体描述信息。
           'subject'         => 'test', //标题
           'order_no'        => Order::build_order_no('D'),//订单号不可重复向支付宝发送
           'amount'          => $money, //单位:元
           'timeout_express' => time() + 15 * 60, //过期时间(当前时间+过期s数),必须大于当前支付请求时间
           'client_ip'       => $request->ip(),
           'goods_type'      => '1',	//0:虚拟商品 1:实物商品
           'return_param'    => 'recharge' //异步通知时原样返回的数据,不需要urlencode。不可包含特殊符号
       ];

       /**
        * Config::ALI_CHANNEL_WEB 	== 'ali_web'| 'wx_qr'
        * Config::ALI_CHARGE	 	== 'ali_charge'
        * Config::ALI_CHANNEL_WEB 	== 'wx_charge'
        */
       $type    = $pay_way == Config::ALI_CHANNEL_WEB ? Config::ALI_CHARGE : Config::WX_CHARGE;
       $config  = config('payment');

       // 调用payment组件接口 返回发起支付的url
       $payment_url = Charge::run($pay_way, $config[$type], $payData);
       
       if ($payment_url) {
           DealRecord::insert([
               'user_id'  => Auth::id(),
               'type'     => 'recharge', //充值
               'order_no' => $payData['order_no'], //订单号
               'status'   => 'waiting', //发起支付默认等待状态 等待支付宝回调
           ]);
       }

       //跳转到支付宝
       header("location:" . $payment_url);

   }

大概看完上面代码有几点疑惑,分别补充:

  • Order::build_order_no(‘D’), 这不过是订单model类的一个方法,你也可以随处定义调用,不过通常来说都有订单,放在订单模型里合理些。
	/**
	 * $letter 可作为订单类型标识 也可以不用 假如你的项目有多种订单类型
	 */
	public static function build_order_no($letter = '')
    {
        return $letter . date('Ymd') . substr(time(), -5) . substr(microtime(), 2, 5) . sprintf('%02d', rand(0, 99));
    }
  • ’return_param’ => ‘recharge’, 异步通知时原样返回的数据,这就比较重要了,因为业务不光只是支付,还可能查询,退款。那么充值的时候带上recharge,回调的时候,根据这个参数就知道是充值操作,调用充值回调后的业务逻辑,进行逻辑的分发。

  • DealRecord::insert 充值记录表,在发起请求之前预先插入一条记录,并默认waiting,支付回调后改为success。这么设计有它的好处,第一我可以知道哪些人发起付款了,第二支付回调后,订单号状态更新为success,如果异常情况多次回调,如果状态不是waiting,那么就拒绝处理业务逻辑(给用户充值)。

CREATE TABLE `deal_records` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `user_id` int(11) unsigned NOT NULL,
  `type` varchar(20) NOT NULL DEFAULT '',  //充值or退款or其他?
  `order_no` varchar(24) NOT NULL DEFAULT '',
  `channel` varchar(20) NOT NULL DEFAULT '', //支付宝or微信?
  `status` varchar(20) NOT NULL DEFAULT '0' COMMENT 'waiting success',
  `serial_no` varchar(64) NOT NULL DEFAULT '', //支付平台返回的
  `pay_amount` decimal(10,2) unsigned NOT NULL DEFAULT '0.00',
  `pay_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  `notify_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  KEY `user_id` (`user_id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;

回调

可以单独创建一个控制器来存放,为什么?因为往往框架都有auth权限中间件,laravel还有csrf防护。我们把一些公共的块分离出来,比如在OtherController里,路由名称为other/notify,那么只需在auth和csrf中间件中排除这个路由:

protected $except = [
		'other/*',
	];
use Payment\Config;
use Payment\Client\Notify;
use Payment\Common\PayException;
use Illuminate\Support\Facades\Log;

	//支付宝微信通知 $channel = ali_charge | wx_charge
    public function notify($channel)
    {
		
        if (!in_array($channel, [Config::ALI_CHARGE, Config::WX_CHARGE])) {
            return '暂不支持';
        }

        $config   = config('payment');

        $callback = new PaymentNotify();

        try {
            $ret = Notify::run($channel, $config[$channel], $callback);
            Log::error($ret);
        } catch (PayException $e) {
            Log::error($channel . ':支付通知:' . $e->getTraceAsString());
        }
        return $ret;
    }

    //同步通知
    public function returnUrl(){
    	return view('alipay');
    }

解释下上边代码:

  • Log::error 做日志是非常有必要的,不管是测试还是正式环境。
  • Notify::run 进行回调逻辑,并通知支付宝结果
  • $callback = new PaymentNotify(); 这是payment组件的一个接口,用来写用户自己的业务逻辑,接下来讲解

PaymentNotify
在项目的一个目录,比如Service目录下(不要纠结,放在一个目录下即可)。

recharge($data);//充值
                break;
            case 'order':
                $result = $this->orderPay($data);//查询订单
                break;
            default:
                $result = false;
        }
        return $result;
    }

    //充值
    public function recharge($data)
    {
        DB::beginTransaction();
        try {
            $order_no = $data['order_no'];
            $record   = DealRecord::where('order_no', $order_no)->first();
            if (!$record || $record->status != 'waiting') {
                Log::error('订单不存在');
                DB::rollBack();
                return false;
            }
            $temp                = [];
            $temp['status']      = $data['trade_state'];
            $temp['channel']     = $data['channel'];
            $temp['notify_time'] = date('Y-m-d H:i:s');
            $temp['pay_amount']  = $data['amount'];
            $temp['pay_time']    = $data['pay_time'];
            $temp['serial_no']   = $data['transaction_id'];
            //$temp['data']        = json_encode($data);

            //生成充值记录 不管成功或者失败
            DealRecord::where('id', $record->id)->update($temp);

            //如果成功 在这里写主要业务逻辑
            if ($data['trade_state'] == 'success')
            {
                //更新用户资产表 user_assets 总充值:
                UserAsset::where('user_id', $record->user_id)->increment('total_recharge', $data['amount']);
                //余额
                UserAsset::where('user_id', $record->user_id)->increment('balance', $data['amount']); 
            }
            DB::commit();
        } catch (\Exception $e) {
            Log::error($e->getTraceAsString());
            Log::error($e->getMessage());
            DB::rollBack();
            return false;
        }
        return true;
    }

    //查询支付订单
    public function orderPay($data)
    {
       
    }

}

资产表结构这里就不贴了,自己定义即可。

支付宝沙箱本地测试

回调地址需要线上地址怎么办?这里给大家介绍个本地也可以进行测试的方法。
叫“内网穿透”,可以将本地地址转成网络地址。
https://natapp.cn
介绍一个支付组件riverslei/payment及本地如何进行支付宝支付测试_第3张图片
在这里插入图片描述
介绍一个支付组件riverslei/payment及本地如何进行支付宝支付测试_第4张图片
购买免费隧道,并且设置后:
在这里插入图片描述
下载后:
在这里插入图片描述
双击运行出现cmd黑窗口;

F:\natapp_windows_amd64_2_3_9>natapp  -authtoken=xxxxxxxx //token在第二图中

介绍一个支付组件riverslei/payment及本地如何进行支付宝支付测试_第5张图片
那么这就是你项目的外网地址了,将其填入支付宝沙箱的回调地址中就可以测试啦!注意:黑窗口不要关闭,重新运行,该地址会更改,你不得不重新配置回调地址,测试过程挂着黑窗口就行了。

沙箱

介绍一个支付组件riverslei/payment及本地如何进行支付宝支付测试_第6张图片
还有密钥的工具这里就不说啦,多看看就能找到。

你可能感兴趣的:(Api)