laravel 微信小程序支付处理

基于eastwechat的小程序支付处理

Posted by 昆山吴彦祖 on 2019.04.23

参考资料:

https://github.com/overtrue/laravel-wechat

https://www.easywechat.com/docs/

基于框架:laravel 5.7 


首先安装easywechat拓展包

1、安装 laravel-wechat( easywehcat 的laravel支持包 )

# Laravel < 5.8
composer require "overtrue/laravel-wechat:~4.0"

# Laravel >= 5.8
composer require "overtrue/laravel-wechat:~5.0"


2、创建配置文件

php artisan vendor:publish --provider="Overtrue\LaravelWeChat\ServiceProvider"


3、修改配置文件参数(config/wechat.php)

微信支付基本配置需要配置如下

WECHAT_PAYMENT_APPID=商户号授权的公共号appid
WECHAT_PAYMENT_MCH_ID=商户号id
WECHAT_PAYMENT_KEY=商户号密钥,在商户号->账户中心->API安全->设置密钥


到这里已经基本配置好了laravel-wechat拓展包,然后根据支付的业务逻辑来创建支付代码

支付代码

我的业务逻辑是:

a)用户点击支付-》进入订单接口-》创建订单+创建微信支付统一订单-》返回到小程序-》小程序请求支付-》支付成功-》微信支付默认回调通知-》更新订单状态

b)待支付订单-》确认支付


普通流程

1、创建小程序订单创建接口并编辑(api/order/store)

// 新增订单
public function store(Request $request){
	//请求参数正确性验证
	
	$pay = null;
	
	//创建一笔商品订单
	if(count($request->mallsArray)){
		// mysql 事务处理订单生成
		DB::transaction(function () use($request,$address,&$pay,&$type) {
			
			//存储订单信息
			$order = new Order();				
			$order->form_id = $request->form_id;//存储微信小程序form-id
			...
			$order->save();
			
			//存储该订单对应产品信息
			foreach($request->mallsArray as $good){									
				$orderItem = new OrderItem();
				...
				$orderItem->save();					
			}
			
			/***  微信支付开始  ***/		
			// 1、创建微信支付统一订单
			$app = app('easywechat.payment');
			
			$unify = $app->order->unify([
				'body' => $order->orderItem[0]->good->title . ' ' . $order->orderItem[0]->num . '...',
				'out_trade_no' => $order->code,
				'total_fee' => ($order->price_express+$order->price_good)*100,
				'trade_type' => 'JSAPI',
				'openid' => Customer::find($request->customer_id)->open_id, // 用户的openid
			]);
			
			//2、储存订单的微信统一支付订单号,防止用户取消支付再付款
			$order->prepay_id = $unify['prepay_id'];
			$order->save();
			
			//3、如果成功生成统一下单的订单,那么进行二次签名,有2种方法
			if ($unify['return_code'] === 'SUCCESS' && !isset($unify['err_code'])) {
				/* $pay = [
					'appId' => config('wechat.payment.default.app_id'),
					'timeStamp' => (string) time(),
					'nonceStr' => $unify['nonce_str'],
					'package' => 'prepay_id=' . $unify['prepay_id'],
					'signType' => 'MD5',
				];

				$pay['paySign'] = generate_sign($pay, config('wechat.payment.default.key')); */

				
				$jssdk = $app->jssdk;
				$pay = $jssdk->bridgeConfig($unify['prepay_id'], false); // 返回数组
			} else {
				//$unify['return_code'] = 'FAIL';
				Log::info($unify);
				
				return response()->json([
					'info' => false,
					'errmsg'=> '支付订单创建失败',
				]);
			}
			
			/***  微信支付结束  ***/
			
		}, 5);
	}
	
	return response()->json([
		'info' => true,
		'pay'=>$pay,
	]);
	
}


2、微信小程序端wxml代码

<form bindsubmit="formSubmit" report-submit="true">
// report-submit设为true获取一个form-id
...
</form>

3、微信小程序端js代码

formSubmit: function (e) {
    var that = this
    //数据验证
	
    //数据验证通过,提交至服务器接口
    wx.request({
      url: app.globalData.baseUrl + '/api/order/store',
      method: 'post',
      data: {
        mallsArray: mallsArray,//请求商品数组
		...
        form_id: e.detail.formId,//小程序表单formid,用于小程序给客户发模板消息
      },
      success(result) {
        console.log(result)
        if (result.data.info) {
			
	//如果是购物车商品,删除购物车相关数据          
          if (wx.getStorageSync('order').type == 'cart') {
			  ....
          }

	//调用微信支付接口
          wx.requestPayment({
            timeStamp: result.data.pay.timeStamp, //注意 timeStamp 的格式
            nonceStr: result.data.pay.nonceStr,
            package: result.data.pay.package,
            signType: result.data.pay.signType,
            paySign: result.data.pay.paySign, // 支付签名
			// 支付成功后的回调函数
            success: function (res) {
              wx.redirectTo({
				  url: '/pages/order/order?status=2',
				})              
            },
            fail: function (res) {
              if (res.errMsg == 'requestPayment:fail cancel') {
                return wx.showToast({
                  icon: 'none',
                  title: '用户取消支付',
                });
              }
            }
          })

        } else {
          wx.showToast({
            title: result.data.errmsg,
            image: '/images/error.png'
          })
        }

      }
    })
  },

4、微信支付默认回调通知控制器代码

public function paySuccess(Request $request){
	$app = app('easywechat.payment');
	
	$response = $app->handlePaidNotify(function($message, $fail) use($app) {
		//Log::info($message);
		
		// 使用通知里的 "微信支付订单号" 或者 "商户订单号" 去自己的数据库找到订单
		$order = Order::where('code',$message['out_trade_no'])->first();

		if (!$order || $order->status!=1) { // 如果订单不存在 或者 订单已经支付过了
			return true; // 告诉微信,我已经处理完了,订单没找到,别再通知我了
		}
		
		//查询订单是否真的支付成功,防止用户模拟post
		$order_search = $app->order->queryByOutTradeNumber($message['out_trade_no']);
		//Log::info($order_search);
		
		// return_code 表示通信状态,不代表支付状态
		if ($message['return_code'] === 'SUCCESS') { 
			// 用户是否支付成功
			if ($message['result_code']=== 'SUCCESS' && $order_search['result_code']=== 'SUCCESS' && $order_search['trade_state']=== 'SUCCESS') {
				$order->paid_at = Carbon::now()->toDateTimeString(); // 更新支付时间为当前时间
				$order->status = 2;

				$order->save(); // 保存订单
			// 用户支付失败
			} elseif ($message['result_code'] === 'FAIL') {
				log::info('用户支付失败,订单号'.$message['out_trade_no']);
			}
		} else {
			return $fail('通信失败,请稍后再通知我');
		}


		return true; // 返回处理完成
	});

	return $response;
	
}


待支付订单流程

1、订单接口代码(api/store)

//立即付款接口
public function pay(Request $request){
	$order = Order::find($request->order_id);
	if(!$order || $order->customer_id != $request->customer_id || $order->status != 1){
		return response()->json([
			'info' => false,
			'errmsg'=>'参数错误',
		]);
	}
	
	//通过查询获取订单状态是否可用 且 未支付
	$app = app('easywechat.payment');
	$order_search = $app->order->queryByOutTradeNumber($order->code);
	//Log::info($order_search);
	if ($order_search['return_code'] === 'SUCCESS' && $order_search['result_code'] === 'SUCCESS' && $order_search['trade_state'] === 'NOTPAY' && $order->prepay_id) {
					
		$jssdk = $app->jssdk;
		$pay = $jssdk->bridgeConfig($order->prepay_id, false); // 返回数组
		$type = $order->type;
	} else {
		//$unify['return_code'] = 'FAIL';
		Log::info($order_search);
		
		return response()->json([
			'info' => false,
			'errmsg'=> '支付订单创建失败',
		]);
	}
	return response()->json([
		'info' => true,
		'pay'=>$pay,
		'type'=>$type,
	]);
}

2、微信端代码基本一样都是通过调用同一个接口请求支付


ps:form-id是用于小程序服务器给用户发送模板消息使用(订单支付成功、订单发货等),一个form-id只能使用一次,有效期7天,每次用户提交订单只会得到一个form-id,所以使用起来很受限
后期会有新的订阅接口代替这个功能


easy_wechat 微信支付