1. 程式人生 > >微信小程式支付PHP例項

微信小程式支付PHP例項

概述

支付主要分這幾個步驟:

Created with Raphaël 2.1.0小程式小程式公眾平臺公眾平臺伺服器伺服器1. 呼叫wx.login()獲得獲取ticket2. 返回ticket3. 帶著ticket,向伺服器請求使用者OpenID4. 呼叫jscode2session API,獲取使用者OpenID5. 成功返回,結果中帶有OpenID6. 返回OpenID7. 發起支付,獲得prepayID8. 呼叫unifiedorder API下單,得到prepayID9. 成功返回,結果中帶有prepayID10. 返回prepayID11. 呼叫wx.requestPayment()完成實際支付

詳細步驟

準備工作

毫無疑問,首先肯定得在頁面上有個支付的按鈕 :-),按鈕繫結事件。

<!--index.wxml-->

<view class="container">
    <button type="primary" bindtap="setLoading">支付</button>
</view>

wx.login獲取ticket

// index.js

var app = getApp()
Page({

  setLoading: function() {
    var that = this
wx.login({ success: function(res) { // 成功的話會返回: // {errMsg: "login:ok", code: "獲取使用者OpenID的ticket"} that.getOpenId(res.code) } }) } })

獲得OpenID

小程式得到ticket後,不能自己獲得使用者OpenID(微信規定的),因此通過伺服器代理

// index.js

var app = getApp()
Page({

  setLoading: function
() {
... } getOpenId: function(jsCode) { var that = this wx.request({ url: 'https://myserver.com/login.php', data: { js_code: jsCode // wx.login()時得到的ticket }, success: function (res) { that.getPrePayId(res.data.openid) } }) }, getPrePayId: function() { // 後面講到 } })

伺服器端程式碼(PHP):

// login.php

$params = [
  'appid' => '小程式的appid',
  'secret' => '小程式的secret',
  'js_code' => $_GET['js_code'], // 小程式傳來的ticket
  'grant_type' => 'authorization_code',
];

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'https://api.weixin.qq.com/sns/jscode2session');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $params);
$output = curl_exec($ch);

if (false === $output) {
  echo 'CURL Error:' . curl_error($ch);
}

echo $output;

$output返回的是個JSON:

{
     "session_key":"abcdefg",
     "expires_in":7200, // 7200秒後失效,小程式要重新通過ticket獲得一次OpenID
     "openid":"abc123" // 使用者的OpenID
}

統一下單

得到OpenID後,再次請求伺服器,讓伺服器代理呼叫統一下單介面:

// index.js

var app = getApp()
Page({

  setLoading: function() { ... },
  getOpenId: function() { ... },

  getPrePayId: function(openId) {
    var that = this
    wx.request({
      url: 'https://myserver.com/pay.php',
      data: {
        openid: openId
      },
      success: function(res) {
        that.pay(res.data)
      }
    })
  },

  pay: function() { // 後面講到 }

})  

服務端程式碼:

$params = [
  'appid' => '小程式的appid',
  'mch_id' => '商戶id',
  // 隨機串,32字元以內
  'nonce_str' => (string) mt_rand(10000, 99999), 
  // 商品名
  'body' => '鞋子', 
  // 訂單號,自定義,32字元以內。多次支付時如果重複的話,微信會返回“重複下單”
  'out_trade_no' => '20170823001' . time(),
  // 訂單費用,單位:分
  'total_fee' => 1,
  'spbill_create_ip' => $_SERVER['REMOTE_ADDR'],
  // 支付成功後的回撥地址,服務端不一定真得有這個地址
  'notify_url' => 'https://myserver.com/notify.php',
  'trade_type' => 'JSAPI',
  // 小程式傳來的OpenID
  'openid' => $_GET['openid'],
];

// 按照要求計算sign

ksort($params);
$sequence = '';
foreach ($params as $key => $value) {
  $sequence .= "$key=$value&";
}

$sequence = $sequence . "key=商戶金鑰";
$params['sign'] = strtoupper(md5($sequence));

// 給微信發出的請求,整個引數是個XML

$xml = '<xml>' . PHP_EOL;
foreach ($params as $key => $value) {
  $xml .= "<$key>$value</$key>" . PHP_EOL;
}
$xml .= '</xml>';

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'https://api.mch.weixin.qq.com/pay/unifiedorder');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
$output = curl_exec($ch);

if (false === $output) {
  echo 'CURL Error:' . curl_error($ch);
}

// 下單成功的話,微信返回個XML,裡面包含prepayID,提取出來

if (0 === preg_match('/<prepay_id><\!\[CDATA\[(\w+)\]\]><\/prepay_id>/', $output, $match)) {
  echo $output;
  exit(0);
}

// 這裡不是給小程式返回個prepayID,而是返回一個包含其他欄位的JSON
// 這個JSON小程式自己也可以生成,放在服務端生成是出於兩個考慮:
// 
// 1. 小程式的appid不用寫在小程式的程式碼裡,appid、secret資訊全部由伺服器管理,比較安全
// 2. 計算paySign需要用到md5,小程式端使用的是JavaScript,沒有內建的md5函式,放在服務端計算md5比較方便

$prepayId = $match[1];
$response= [
  'appId' => '小程式appid',
  // 隨機串,32個字元以內
  'nonceStr' => (string) mt_rand(10000, 99999),
  // 微信規定
  'package' => 'prepay_id=' . $prepayId,
  'signType' => 'MD5',
  // 時間戳,注意得是字串形式的
  'timeStamp' => (string) time(),
];
$sequence = '';
foreach ($response as $key => $value) {
  $sequence .= "$key=$value&";
}
$response['paySign'] = strtoupper(md5("{$sequence}key=商戶金鑰"));

echo json_encode($response);

wx.requestPayment() 支付

最後,小程式發起實際的支付,介面上會彈出支付視窗

// index.js

var app = getApp()
Page({

  setLoading: function() { ... },
  getOpenId: function() { ... },
  getPrePayId: function() { ... },

  // data是服務端返回的JSON
  // 加上success、fail回撥後,正好符合wx.requestPayment()引數的格式

  pay: function(data) {
    data.success = function(res) {
      // 使用者支付成功後的程式碼
    }
    data.fail = function(res) {
      // 使用者支付失敗後的程式碼
    }
    wx.requestPayment(data)
  }

})