大致步骤:
客户端发起请求 》服务器检测订单是否有效、库存 》 生成签名 》返回给微信客户端 》发起支付 》 支付回调 》处理回调 》
》更新支付状态
1、检测订单有效性
private function checkOrderValid()
{
//检测订单号是否存在
$order = OrderModel::where('id','=',$this->orderID)->find();
if (!$order){
throw new OrderException();
}
//检测订单号是否和用户匹配
if (!TokenService::isValidOperate($order->user_id)){
throw new TokenException(['msg'=>'订单与用户不匹配','errorCode'=>10003]);
}
//检测订单是否已经被支付过
if ($order->status != OrderStatusEnum::UNPAID){
throw new OrderException(['msg'=>'订单已经支付','errorCode'=>80003,'code'=>400]);
}
//通过
$this->orderNo = $order->order_no;
return true;
}
public static function isValidOperate($checkedUID)
{
if (!$checkedUID){
throw new Exception('检查用户uid是否存在');
}
$currentOperateUID = self::getCurrentUid();
if($currentOperateUID == $checkedUID){
return true;
}
return false;
}
2、检测库存
$orderService = new OrderService();
//检测库存
$status = $orderService->checkOrderStock($this->orderID);
if (!$status['pass']){
return $status;//不通过
}
public function checkOrderStock($orderID)
{
$oProducts = OrderProduct::where('order_id','=',$orderID)->select();
$this->oProducts = $oProducts;
$this->products = $this->getProductsByOrder($oProducts);
$status = $this->getOrderStatus();
return $status;
}
private function getProductsByOrder($oProducts)
{
$oPIDs = [];
foreach ($oProducts as $oProduct) {
array_push($oPIDs,$oProduct['product_id']);
}
$products = Product::all($oPIDs)
->visible(['id','price','stock','name','main_img_url'])
->toArray();
return $products;
}
private function getOrderStatus()
{
$status = [
'pass' => true,
'orderPrice' => 0,//订单总价格
'totalCount' => 0,//所有商品数量
'pStatusArray' => [],//保存商品信息
];
foreach ($this->oProducts as $oProduct) {
$pStatus = $this->getProductStatus($oProduct['product_id'],$oProduct['count'],$this->products);
if (!$pStatus['haveStock']){
$status['pass'] = false;
}
$status['orderPrice'] += $pStatus['totalPrice'];
$status['totalCount'] += $pStatus['count'];
array_push($status['pStatusArray'],$pStatus);
}
return $status;
}
private function getProductStatus($oPID,$oCount,$products)
{
$pIndex = -1;
//一个订单内某一个商品的信息
$pStatus = [
'id' => null,
'haveStock' => false,
'count' => 0,
'name' => '',
'totalPrice' => 0, //某一个商品的总价格
];
for ($i = 0; $i'id为'.$oPID.'商品不存在,创建订单失败']);
}else{
$product = $products[$pIndex];
$pStatus['id'] = $product['id'];
$pStatus['name'] = $product['name'];
$pStatus['count'] = $oCount;
$pStatus['totalPrice'] = $product['price'] * $oCount;
if ($product['stock'] - $oCount >= 0){
$pStatus['haveStock'] = true;
}
}
return $pStatus;
}
3、发起支付
private function makeWxPreOrder($totalPrice)
{
$openid = TokenService::getCurrentTokenVar('openid');
if (!$openid){
throw new TokenException();
}
$wxOrderData = new \WxPayUnifiedOrder();
$wxOrderData->SetOut_trade_no($this->orderNo);
$wxOrderData->SetTrade_type('JSAPI');
$wxOrderData->SetTotal_fee($totalPrice*100);
$wxOrderData->SetBody('零食商贩');
$wxOrderData->SetOpenid($openid);
$wxOrderData->SetNotify_url(config('secure.pay_back_url'));
return $this->getPaySignature($wxOrderData);
}
private function getPaySignature($wxOrderData)
{
$config = new \WxPayConfig();
$wxOrder = \WxPayApi::unifiedOrder($config,$wxOrderData);
if ($wxOrder['return_code']!='SUCCESS'||$wxOrder['result_code']!='SUCCESS'){
Log::record($wxOrder,'error');
Log::record('获取预支付订单失败','error');
}
$this->recordPreOrder($wxOrder);
$signature = $this->sing($wxOrder);
return $signature;
}
private function sing($wxOrder)
{
$jsApiPayData = new \WxPayJsApiPay();
$jsApiPayData->SetAppid(config('wx.app_id'));
$jsApiPayData->SetTimeStamp((string)time());
$rand = md5(time().mt_rand(0,1000));
$jsApiPayData->SetNonceStr($rand);
$jsApiPayData->SetPackage($wxOrder['prepay_id='.'prepay_id']);
$jsApiPayData->SetSignType('md5');
$sign = $jsApiPayData->MakeSign();
$rawValues = $jsApiPayData->GetValues();
$rawValues['paySign'] = $sign;
unset($rawValues['appId']);
return $rawValues;
}
private function recordPreOrder($wxOrder)
{
OrderModel::where('id','=',$this->orderID)
->update(['prepay_id'=>$wxOrder['prepay_id']]);
}
4、客户端支付后,微信服务器会将支付结果异步回调给我们服务器的通知地址。做出相应处理
public function NotifyProcess($data,&$msg)
{
if($data['result_code'] == 'SUCCESS'){
$orderNo = $data['out_trade_no'];
Db::startTrans();
try{
$order = OrderModel::where('order_no','=',$orderNo)
->lock(true)
->find();
if ($order->status == OrderStatusEnum::UNPAID){
//如果处理较慢,则微信会发过来第二个请求,会造成库存量减小多次的情况
$service = new OrderService();
$stockStatus = $service->checkOrderStock($order->id);
if ($stockStatus['pass']){
$this->updateOrderStatus($order->id,true);
$this->reduceStock($stockStatus);
}else{
$this->updateOrderStatus($order->id,false);
}
}
Db::commit();
return true;
}catch (Exception $ex){
Db::rollback();
Log::error($ex);
return false;
}
}else{
return true;
}
}
//更新订单状态
private function updateOrderStatus($orderID,$success)
{
// $success 为1 即代表:库存够;否则不够
$status = $success ? OrderStatusEnum::PAID : OrderStatusEnum::PAID_BUT_OUT_OF;
OrderModel::where('id','=',$orderID)->update(['status',$status]);
return true;
}
//减少库存
private function reduceStock($stockStatus)
{
foreach ($stockStatus['pStatusArray'] as $singlePStatus) {
Product::where('id','=',$singlePStatus['id'])->setDec('stock',$singlePStatus['count']);
}
return true;
}