小程序用TP5.1從下訂單到實現(xiàn)微信支付

這篇文章主要是想整理一下學習了微信小程序商城構(gòu)建全棧應(yīng)用微信支付時候的思路和實現(xiàn)的方法妖啥。不然老是忘記蛤签。
主要的步驟有
1.獲取Token拴清。
2.根據(jù)Token下訂單泻蚊。
3.創(chuàng)建訂單成功后躲舌,繼續(xù)發(fā)送下單的接口到后臺,后臺調(diào)用微信的SDK接口性雄,將時間戳没卸,加密的羹奉,簽名,appid约计,商戶號等信息發(fā)送給微信诀拭,微信返回信息到后臺,后臺再傳給小程序煤蚌,小程序再調(diào)用微信的接口拉起收款耕挨。
4.微信收款成功后,微信會根據(jù)后臺之前傳入的api地址來給系統(tǒng)后臺異步發(fā)送微信支付成功的信息尉桩。然后在觸發(fā)了該api之后筒占,系統(tǒng)更改訂單的狀態(tài),扣除商品的數(shù)量蜘犁。

1.獲取Token:

Token是必須的翰苫,有些隱秘的資源需要Token來獲取。根據(jù)token也可以分出權(quán)限來这橙,權(quán)限的不同則表示了你能否獲得該資源革骨。

通過微信傳過來的code換取appid,然后再根據(jù)appid查找數(shù)據(jù)庫中是否有該用戶析恋,沒有則創(chuàng)建

// 發(fā)送小程序的code到指定的微信地址從而獲取該用戶的appid
    $res = curl_get($this->loginUrl);
//        true 為 字符串轉(zhuǎn)換成數(shù)組良哲,false為轉(zhuǎn)換成對象
        $wxResult = json_decode($res, true);
        if (empty($wxResult)) {
            throw new Exception('獲取session_key及openID時異常,微信內(nèi)部錯誤');
        } else {
            $loginFail = array_key_exists('errcode', $wxResult);
            if ($loginFail) {
                throw new weChatException([
                    'errorcode' => $wxResult['errcode'],
                    'msg' => $wxResult['errmsg']
                ]);
            } else {
                return $this->grantToken($wxResult);
            }
        }

判斷是否有該用戶

    $openid = $wxResult['openid'];
        $user = User::getByOpenID($openid);
        if ($user) {
            $uid = $user->id;
        } else {
            $uid = $this->newUser($openid);
        }

完成了之后助隧,將用戶的信息放入cache中筑凫,生成token,并且以token作為key并村,用戶的信息作為value傳入cache

制作Value

private function prepareCacheValue($wxResult, $uid)
    {
        $cacheValue = $wxResult;
        $cacheValue['uid'] = $uid;
// 指定用戶權(quán)限的數(shù)值巍实,根據(jù)數(shù)值的不同,表示能否訪問該權(quán)限
        $cacheValue['scope'] = ScopeEnum::User;
        return $cacheValue;
    }

生成Token傳入cache

public static function generateToken()
    {
//        32個字符組成一組隨機字符串
//        總共三組
//        隨機字符串
//        時間戳
//        鹽哩牍,放入config中
        $randChars = getRandChars(32);
        $timestamp = $_SERVER['REQUEST_TIME'];
        $salt = config('secure.salt');
        return md5($randChars . $timestamp . $salt);
 }
// 傳入cache中
        $key = $this->generateToken();
        $value = json_encode($cacheValue);
        $expire = config('setting.token_expire_in');
        $request = Cache::set($key,$value,$expire);
        if (!$request) {
            throw new TokenException([
                'msg' => '服務(wù)器緩存異常',
                'errorcode' => 10005
            ]);
        }
        return $key;

2.根據(jù)Token下訂單

通過Token獲取到Cache里面的uid棚潦,通過此來進行下訂單。

傳輸訂單的接口為

[
  { 
      id:1,
      count:1
  },
 { 
      id:2,
      count:2
  }
]

通過商品id查詢商品

private function getProductsByOrder($oProducts)
 {
        $oPids = [];
        foreach ($oProducts as $item) {
            $oPids[] = $item['product_id'];
        }
        $products = Product::all($oPids)->visible(['id', 'name', 'price', 'stock', 'main_img_url'])->toArray();
        return $products;
 }

檢測商品庫存是否足夠并且獲取訂單價格和數(shù)量膝昆,將下單商品的詳細信息保存到數(shù)組中

    private function getOrderStatus()
    {
        $status = [
            'pass' => true,
            'orderPrice' => 0,
            'orderCount' => 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['orderCount'] += $pStatus['count'];
            array_push($status['pStatusArray'], $pStatus);
        }
        return $status;
}
//    獲取商品的狀態(tài)
    private function getProductStatus($oPid, $oCount, $products)
    {
        $pIndex = -1;
        $pStatus = [
            'id' => null,
            'haveStock' => false,
            'count' => 0,
            'name' => '',
            'totalPrice' => 0
        ];
        for ($i = 0; $i < count($products); $i++) {
            if ($oPid == $products[$i]['id']) {
                $pIndex = $i;
            }
        }
        if ($pIndex == -1) {
            throw new OrderException([
                'msg' => 'id為' . $oPid . '的商品不存在丸边,創(chuàng)建訂單失敗'
            ]);
        } else {
            $product = $products[$pIndex];
            $pStatus['id'] = $product['id'];
            $pStatus['count'] = $oCount;
            $pStatus['name'] = $product['name'];
            $pStatus['totalPrice'] = $product['price'] * $oCount;
            if ($product['stock'] >= $oCount) {
                $pStatus['haveStock'] = true;
            }
        }
        return $pStatus;
    }

檢測商品是否通過檢查,通過則生成訂單編號并且生成訂單快照荚孵,不通過則直接返回到客戶端妹窖。

由于商品和價格都可能會發(fā)生變化,所以使用外鏈的方法會出現(xiàn)錯誤收叶,所以需要把當時商品的信息記錄下來骄呼。

創(chuàng)建訂單編號

    public static function makeOrderNum()
    {
        $yCode = array('A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J');
        $orderSn = $yCode[intval(date('Y') - 2018)] . strtoupper(dechex(date('m'))) . date('d') . substr(time(), -5) . substr(microtime(), 2, 5) . sprintf('%02d', rand(0, 99));
        return $orderSn;
    }

生成訂單快照

    private function snapOrder($status)
    {
        $orderSnap = [
            'orderPrice' => 0,
            'totalCount' => 0,
            'pStatus' => [],
            'snapAddress' => null,
            'snapName' => '',
            'snapImg' => ''
        ];
        $orderSnap['orderPrice'] = $status['orderPrice'];
        $orderSnap['totalCount'] = $status['orderCount'];
        $orderSnap['pStatus'] = $status['pStatusArray'];
        $orderSnap['snapAddress'] = json_encode($this->getUserAddress());
        $orderSnap['snapName'] = $this->products[0]['name'];
        if (count($this->products) > 1) {
            $orderSnap['snapName'] .= '等';
        }
        $orderSnap['snapImg'] = $this->products[0]['main_img_url'];
        return $orderSnap;
    }

做完這一切的步驟后,就可以插入數(shù)據(jù)庫了

主要是錄入訂單信息,和錄入訂單商品信息(id蜓萄,關(guān)聯(lián)的訂單id隅茎,數(shù)量)

    private function createOrder($orderSnap)
    {
        Db::startTrans();
        try {
            $orderNum = self::makeOrderNum();
            $order = new OrderModel();
            $order->order_no = $orderNum;
            $order->user_id = $this->uid;
            $order->total_price = $orderSnap['orderPrice'];
            $order->snap_img = $orderSnap['snapImg'];
            $order->snap_name = $orderSnap['snapName'];
            $order->total_count = $orderSnap['totalCount'];
            $order->snap_items = json_encode($orderSnap['pStatus']);
            $order->snap_address = json_encode(UserAddress::where('user_id', 'eq', $this->uid)->find()->toArray());
            $order->save();

            $orderId = $order->id;
            $createTime = $order->create_time;
            foreach ($this->oProducts as &$p) {
                $p['order_id'] = $orderId;
            }
            $orderProduct = new OrderProduct();
            $orderProduct->saveAll($this->oProducts);
            Db::commit();
            return [
                'order_no' => $orderNum,
                'order_id' => $orderId,
                'create_time' => $createTime
            ];
        } catch (Exception $ex) {
            Db::rollback();
            throw $ex;
        }
    }

3.根據(jù)訂單號實現(xiàn)預支付

根據(jù)訂單號來判斷是否存在該訂單號,該訂單號是否屬于該token的uid嫉沽,該訂單號的狀態(tài)是否為未支付患膛,最后還需要再檢查庫存。
注:部分和上面方法通用耻蛇,需寫為一個通用的方法踪蹬。
實現(xiàn)了上面的步驟之后,就可以使用起微信的SDK了臣咖。
將微信的文件放在了該目錄下:


由于TP5.1取消了Loader載入的方法跃捣,所以我找了好久,使用的是Env來獲取微信文件的地址夺蛇。通過此來加載SDK的文件疚漆。

include Env::get('root_path') . 'extend/wxPay/WxPay.Api.php';

注:微信支付的WxPay.config改變了,我們需要繼承該方法刁赦,填入自己的appid娶聘,appsecret和mmid等信息才行。
引入微信的SDK后甚脉,首先需要做的就是設(shè)置各種屬性丸升。

        $wxOrderData = new \WxPayUnifiedOrder();
// 會通過異步傳回來給我們
        $wxOrderData->SetOut_trade_no($this->orderNum);
        $wxOrderData->SetTrade_type('JSAPI');
        $wxOrderData->SetTotal_fee($totalPrice * 100);
        $wxOrderData->SetBody('零食商販');
        $wxOrderData->SetOpenid($openid);
// 支付成功后,微信的回調(diào)牺氨,要用post提交
        $wxOrderData->SetNotify_url('http://paysdk.weixin.qq.com/notify.php');
        return $this->getPaySignature($wxOrderData);

設(shè)置好了微信需要的信息之后狡耻,就開始發(fā)送到微信了,這里需要將繼承的config傳入里面發(fā)送給微信

    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');
        }
// 記錄prepay_id
        $this->recordPayOrder($wxOrder);
        $signature = $this->sign();
        return $signature;
    }

如果獲取到的信息正確猴凹,就需要將需要的信息返回給客戶端夷狰,客戶端根據(jù)此信息來調(diào)用微信的支付接口。

    private function sign($wxOrder)
    {
        $jsApiPayData = new \WxPayJsApiPay();
        $jsApiPayData->SetAppid(config('wx.appid'));
        $jsApiPayData->SetTimeStamp((string)time());
        $rand = md5(time() . mt_rand(0, 1000));
        $jsApiPayData->SetNonceStr($rand);
        $jsApiPayData->SetPackage('prepay_id=' . $wxOrder['prepay_id']);
        $jsApiPayData->SetSignType('md5');

        $sign = $jsApiPayData->MakeSign();
        $rawValues = $jsApiPayData->GetValues();
        $rawValues['paySign'] = $sign;
        unset($rawValues['appId']);
        return $rawValues;

    }

根據(jù)微信支付的信息郊霎,返回給客戶端相應(yīng)的信息沼头。SDK在設(shè)置完之后,通過GetValue可以傳換成數(shù)組


客戶端得到數(shù)據(jù)后調(diào)用該微信API就能實現(xiàn)微信支付了书劝。

4.微信支付成功后的回調(diào)處理

由于微信返回的是xml格式的數(shù)據(jù)进倍,所以同樣可以通過sdk來進行處理。
需要做的是繼承WxPayNotify庄撮,并且改寫里面的NotifyProcess方法背捌。然后調(diào)用Handle方法即可。

里面主要的步驟就是洞斯,檢測庫存,減庫存,修改訂單信息這幾步烙如。

    public function NotifyProcess($objData, $config, &$msg)
    {
        if ($objData['result_code'] == 'SUCCESS') {
            $orderNo = $objData['out_trade_no'];
            Db::startTrans();
            try {
                $order = OrderModel::where('order_no', 'eq', $orderNo)->find();
                if ($order->status == 1) {
                    $orderService = new OrderService();
                    $stockStatus = $orderService->checkOrderStock($order->id);
                    if ($stockStatus['pass']) {
                        $this->updateOrderStatus($order->id, true);
                        $this->reduceStock($stockStatus['pStatusArray']);
                    } else {
                        $this->updateOrderStatus($order->id, false);
                    }
                }
                Db::commit();
                return true;
            } catch (Exception $ex) {
                Db::rollback();
                Log::error($ex);
                return false;
            }
        } else {
            return true;
        }
    }

需要返回True來表示接受成功么抗,返回false,微信會隔一段時間再發(fā)送一次POST請求亚铁。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末蝇刀,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子徘溢,更是在濱河造成了極大的恐慌吞琐,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,348評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件然爆,死亡現(xiàn)場離奇詭異站粟,居然都是意外死亡,警方通過查閱死者的電腦和手機曾雕,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,122評論 2 385
  • 文/潘曉璐 我一進店門奴烙,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人剖张,你說我怎么就攤上這事切诀。” “怎么了搔弄?”我有些...
    開封第一講書人閱讀 156,936評論 0 347
  • 文/不壞的土叔 我叫張陵幅虑,是天一觀的道長。 經(jīng)常有香客問我顾犹,道長翘单,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,427評論 1 283
  • 正文 為了忘掉前任蹦渣,我火速辦了婚禮哄芜,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘柬唯。我一直安慰自己认臊,他們只是感情好,可當我...
    茶點故事閱讀 65,467評論 6 385
  • 文/花漫 我一把揭開白布锄奢。 她就那樣靜靜地躺著失晴,像睡著了一般。 火紅的嫁衣襯著肌膚如雪拘央。 梳的紋絲不亂的頭發(fā)上涂屁,一...
    開封第一講書人閱讀 49,785評論 1 290
  • 那天,我揣著相機與錄音灰伟,去河邊找鬼拆又。 笑死儒旬,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的帖族。 我是一名探鬼主播栈源,決...
    沈念sama閱讀 38,931評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼竖般!你這毒婦竟也來了甚垦?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,696評論 0 266
  • 序言:老撾萬榮一對情侶失蹤涣雕,失蹤者是張志新(化名)和其女友劉穎艰亮,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體挣郭,經(jīng)...
    沈念sama閱讀 44,141評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡迄埃,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,483評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了丈屹。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片调俘。...
    茶點故事閱讀 38,625評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖旺垒,靈堂內(nèi)的尸體忽然破棺而出彩库,到底是詐尸還是另有隱情,我是刑警寧澤先蒋,帶...
    沈念sama閱讀 34,291評論 4 329
  • 正文 年R本政府宣布骇钦,位于F島的核電站,受9級特大地震影響竞漾,放射性物質(zhì)發(fā)生泄漏眯搭。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,892評論 3 312
  • 文/蒙蒙 一业岁、第九天 我趴在偏房一處隱蔽的房頂上張望鳞仙。 院中可真熱鬧,春花似錦笔时、人聲如沸棍好。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽借笙。三九已至,卻和暖如春较锡,著一層夾襖步出監(jiān)牢的瞬間业稼,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工蚂蕴, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留低散,地道東北人俯邓。 一個月前我還...
    沈念sama閱讀 46,324評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像谦纱,于是被迫代替她去往敵國和親看成。 傳聞我的和親對象是個殘疾皇子君编,可洞房花燭夜當晚...
    茶點故事閱讀 43,492評論 2 348