WeChat アプレット決済 + php バックエンド (コールバック、注文クエリ、注文情報ストレージ) のシンプルな実装

この記事では、支払いを開始するミニプログラム、バックエンド注文の発注、注文クエリ、注文コールバック、注文保管などの操作を含む、WeChat ミニプログラム支払いの開発方法について説明します。

プロセス

WeChat アプレットは注文パラメータを取得します -> バックエンドへの注文に同意するリクエストを開始します -> 注文パラメータを取得します -> アプレットは API を呼び出して支払いを開始します -> 支払いが完了します -> コールバックを送信します -> 支払い結果が保存されます-> 注文の支払い状況を問い合わせます。

バックエンドコード

getOpenid.php

設定する必要があるパラメータは次のとおりです $appid、$secret、orderPrice。そのうちの $appid、$secret、orderPrice 2 つのパラメータはアプレットのorderPrice 注文金額 (元単位) です。

<?php

// ページエンコーディング
header("content-type:application/json");

// アプレットによって渡された CODE を取得
$code = trim($_GET['code']);

// アプレット appid
$appid = "ここに記入してください";

// アプレット appscret
$secret = "ここにシークレットを入力してください";

// 認可されたログイン API インターフェース
$api = "https://api.weixin.qq.com/sns/jscode2session?appid=$appid&secret=$secret&js_code=$code&grant_type=authorization_code";

// リクエストを開始
$result = file_get_contents($api);

// 获取openid
$arr_result = json_decode($result, true);
$openid = $arr_result["openid"];

// 情報を返します
$result = array(
    'code' => 200,
    'msg' => 'openid を正常に取得しました',
    'openid' => $openid,
    'orderPrice' => 0.01 // 単位: 元
);

// 出力 JSON
echo json_encode($result,JSON_UNESCAPED_UNICODE);

?>

creatOrder.php

構成する必要があるパラメータはコード内に記述されています。
$appid、$mchid、$xlid、$data['notify_url']、$set_body、$price

<?php

// 页面编码
header('Content-type:text/html; Charset=utf-8');
ini_set('date.timezone','Asia/Shanghai');

// 统一下单
function wechartAddOrder($name,$ordernumber,$money,$openid,$timeStamp,$noncestr){
    $url = "https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi";
    $urlarr = parse_url($url);

    // 需要配置以下参数
    // 需要配置以下参数
    // 需要配置以下参数
    // 需要配置以下参数
    // 需要配置以下参数
    $appid = '填写你的小程序appid'; // appID
    $mchid = '填写你的微信支付商户号'; // 商户ID
    $xlid = '填写你的秘钥序列号'; // 秘钥序列号 可在这个网址中查询 https://myssl.com/cert_decode.html

    $data = array();
    $time = $timeStamp;
    $data['appid'] = $appid;
    $data['mchid'] = $mchid;
    $data['description'] = $name; // 商品描述
    $data['out_trade_no'] = $ordernumber; // 订单编号

    // 异步订单回调线上地址
    // 就是notify.php这个文件的线上URL
    // 例如你的notify.php所在服务器的位置是wwwroot/xcxpay/notify.php
    // 你的域名是https://www.qq.com
    // 那么应该按照这个格式填写URL:https://www.qq.com/xcxpay/notify.php
    $data['notify_url'] = "填写notify.php这个文件的线上URL";

    $data['amount']['total'] = intval($money * 1); // 金额(单位:分)
    $data['payer']['openid'] = $openid; // 用户openID
    $data = json_encode($data); 
    $key = getSign($data,$urlarr['path'],$noncestr,$time); // 签名
    $token = sprintf('mchid="%s",serial_no="%s",nonce_str="%s",timestamp="%d",signature="%s"',$mchid,$xlid,$noncestr,$time,$key); // 头部信息
 
    $header  = array(
        'Content-Type:'.'application/json; charset=UTF-8',
        'Accept:application/json',
        'User-Agent:*/*',
        'Authorization: WECHATPAY2-SHA256-RSA2048 '.$token
    );  
    $ret = curl_post_https($url,$data,$header);
    $ret = ltrim($ret,'{"prepay_id":"');
    $ret = rtrim($ret,'}"');
    
    // 微信支付(小程序)签名
    $str = getWechartSign($appid,$timeStamp,$noncestr,'prepay_id='.$ret);
    
    // 需返回的一些预支付参数
    $arr = array(
        'appid' => $appid,
        'timestamp' => $timeStamp,
        'package' => 'prepay_id='.$ret,
        'paySign' => $str,
        'noncestr' => $noncestr,
        'orderNum' => $ordernumber,
        'orderPrice' => intval($money * 1) // 可用number_format转换金额
    );
    exit(json_encode($arr));
}

// 微信支付签名
function getSign($data=array(),$url,$randstr,$time){
    $str = "POST"."\n".$url."\n".$time."\n".$randstr."\n".$data."\n";
    $key = file_get_contents('apiclient_key.pem');// 在商户平台下载的秘钥
    $str = getSha256WithRSA($str,$key);
    return $str;
}
 
// 调起支付的签名
function getWechartSign($appid,$timeStamp,$noncestr,$prepay_id){
    $str = $appid."\n".$timeStamp."\n".$noncestr."\n".$prepay_id."\n";
    $key = file_get_contents('apiclient_key.pem');
    $str = getSha256WithRSA($str,$key);
    return $str;
}
 
function getSha256WithRSA($content, $privateKey){
    $binary_signature = "";
    $algo = "SHA256";
    openssl_sign($content, $binary_signature, $privateKey, $algo);
    $sign = base64_encode($binary_signature);
    return $sign;
}
 
/* PHP CURL HTTPS POST */
function curl_post_https($url,$data,$header){ // 模拟提交数据函数
    $curl = curl_init(); // 启动一个CURL会话
    curl_setopt($curl, CURLOPT_URL, $url); // 要访问的地址
    curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0); // 对认证证书来源的检查
    curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 1); // 从证书中检查SSL加密算法是否存在
    curl_setopt($curl, CURLOPT_USERAGENT, $_SERVER['HTTP_USER_AGENT']); // 模拟用户使用的浏览器
    curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1); // 使用自动跳转
    curl_setopt($curl, CURLOPT_AUTOREFERER, 1); // 自动设置Referer
    curl_setopt($curl, CURLOPT_POST, 1); // 发送一个常规的Post请求
    curl_setopt($curl, CURLOPT_POSTFIELDS, $data); // Post提交的数据包
    curl_setopt($curl, CURLOPT_TIMEOUT, 30); // 设置超时限制防止死循环
    curl_setopt($curl, CURLOPT_HEADER, 0); // 显示返回的Header区域内容
    curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); // 获取的信息以文件流的形式返回
 
    curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
    $tmpInfo = curl_exec($curl); // 执行操作
    if (curl_errno($curl)) {
        echo 'Errno'.curl_error($curl);//捕抓异常
    }
    curl_close($curl); // 关闭CURL会话
    return $tmpInfo; // 返回数据,json格式
}

// 生成noncestr
function creatnoncestr($length){
    $str = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890';
    $randStr = str_shuffle($str);
    $rands= substr($randStr,0,$length);
    return $rands;
}

// 商品名称
$set_body = '小程序支付';

// 支付金额(单位:分)
// 还需要去getOpenid.php里面修改这个价格才会在小程序显示准确的价格
$price = 1;

// 商户自定义订单号
$out_trade_no = date('Ymd').time();

// 时间戳
$timeStamp = time();

// 支付用户
$openid = trim($_GET['openid']);

// 随机字符串
$noncestr = creatnoncestr(8);
 
// 创建订单
wechartAddOrder($set_body,$out_trade_no,$price,$openid,$timeStamp,$noncestr);

?>

v3OrderCallBack.php
ファイルは支払いコールバックの署名検証であり、構成する必要があるパラメータは次のとおりです。 ApiV3Key

<?php

class AesUtil
{
    public $aesKey = '填写你的ApiV3Key,在商户平台设置并获取';
    const KEY_LENGTH_BYTE = 32;
    const AUTH_TAG_LENGTH_BYTE = 16;
    public
    function __construct()
    {
        $aesKey = '填写你的ApiV3Key,在商户平台设置并获取';
        if (strlen($aesKey) != self::KEY_LENGTH_BYTE) {
            throw new InvalidArgumentException('无效的ApiV3Key,长度应为32个字节');
        }
        $this->aesKey = $aesKey;
    }

    public function decryptToString($associatedData, $nonceStr, $ciphertext)
    {
        $ciphertext = \base64_decode($ciphertext);
        if (strlen($ciphertext) <= self::AUTH_TAG_LENGTH_BYTE) {
            return false;
        }
        if (function_exists('\sodium_crypto_aead_aes256gcm_is_available') && \sodium_crypto_aead_aes256gcm_is_available()) {
            return \sodium_crypto_aead_aes256gcm_decrypt($ciphertext, $associatedData, $nonceStr, $this->aesKey);
        }
        if (function_exists('\Sodium\crypto_aead_aes256gcm_is_available') && \Sodium\crypto_aead_aes256gcm_is_available()) {
            return \Sodium\crypto_aead_aes256gcm_decrypt($ciphertext, $associatedData, $nonceStr, $this->aesKey);
        }
        if (PHP_VERSION_ID >= 70100 && in_array('aes-256-gcm', \openssl_get_cipher_methods())) {
            $ctext = substr($ciphertext, 0, -self::AUTH_TAG_LENGTH_BYTE);
            $authTag = substr($ciphertext, -self::AUTH_TAG_LENGTH_BYTE);

            return \openssl_decrypt($ctext, 'aes-256-gcm', $this->aesKey, \OPENSSL_RAW_DATA, $nonceStr,
                $authTag, $associatedData);
        }
        throw new \RuntimeException('AEAD_AES_256_GCM需要PHP 7.1以上或者安装libsodium-php');
    }
}

Db.php
データベース構成。

<?php

/**
 * データベース構成ファイル
 * BY TANKING
 * 2023-1-2
 **/
 
$dbhost = "データベース アドレス";
$dbuser = "データベース アカウント";
$dbpwd = "データベース パスワード";
$dbname = "データベース名" ;

?>

データベースのテーブル構造は次のとおりです。

テーブルの作成は、次の SQL ステートメントを通じて実行できます。

SQL文

CREATE TABLE `xcxpay_order` (
  `id` int(10) NOT NULL COMMENT '自動インクリメントID',
  `order_num` varchar(32) DEFAULT NULL COMMENT '注文番号',
  `openid` varchar(32) DEFAULT NULL COMMENT 'openid ' ,
  `order_total` varchar(10) DEFAULT NULL COMMENT '注文金額 (セント)',
  `order_time` タイムスタンプ NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '注文時間'
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

証明書ファイル


また、販売者プラットフォームに移動して証明書ファイルをダウンロードする必要があります。

入手方法:

アプレットコード

pay.wxml


  <view class="orderInfo">{ {orderPrice}} 元を支払う必要があります
  </view> 
<view class="paybtn" bindingtap="Reqpay">今すぐ支払います</view> 
<view class="paySuccess" wx: if ="{ 
  
  {paySuccess == true}}">支払いが成功しました</view>

pay.js は
ドメイン名とバックエンド ディレクトリ名を設定する必要があります。

Page({       データ: {             paySuccess:false       },


      // ページロードを入力
      onLoad(e) {             var that = this;

            // ユーザーを取得します
            wx.login({                   success: function(res) {                         if (res.code) {                               wx.request({                                     url: "https://ドメイン名/ディレクトリ/getOpenid.php?code="+res .code、                                     ヘッダー: {"content-type": "application/json"}、                                     成功: function(res) {





                                          console.log(res.data)
                                          // openid と注文金額を取得
                                          that.setData({                                                 openid:res.data.openid,                                                 orderPrice:res.data.orderPrice                                           })                                     }                               });                         }                   }             })       },








      // 支払いを開始します
      Reqpay: function(){             var that = this;             wx.request({                   url: "https://ドメイン名/ディレクトリ/creatOrder.php?openid="+that.data.openid,                   header: { 'content-type': 'application/json'},                   成功 (res) {




                        console.log(res.data)
                        // 支払いパラメータをリクエストします
                        var timestamp_ = res.data.timestamp;
                        var noncestr_ = res.data.noncestr;
                        var package_ = res.data.package;
                        var paySign_ = res.data.paySign;

                        // 注文パラメータ
                        var orderNum = res.data.orderNum; // 注文番号
                        
                        wx.requestPayment({                               timeStamp: timestamp_.toString(),                               nonceStr: noncestr_,                               package: package_,                               signType: 'MD5',                               paySign: paySign_,                               success ( res) {                                     console.log(res)                                     if(res.errMsg == 'requestPayment:ok'){







                                          // 支払いは成功しました
                                          console.log('支払いは成功しました')
                                          that.setData({                                                 paySuccess:true                                           })

                                          // requestPayment:ok の結果を支払いの成功を判断する基準として使用しないでください
                                          // 注文の支払いの実際の結果を判断する必要がある場合は、引き続き注文の支払い結果をクエリするためのロジックを追加してください
                                    }
                              }
                        })
                  }
            })
      }
})

pay.wxss

.orderInfo{       幅: 88%;       高さ: 100ピクセル;       背景: #eee;       境界半径: 20px;       マージン: 30px 自動 0;       行の高さ: 100px;       テキスト整列: 中央;       フォントサイズ: 25px; .paybtn {       幅: 88%;       高さ: 55ピクセル;       行の高さ: 55px;       背景: #f7c58a;       境界半径: 10px;       マージン: 10px 自動 0;       テキスト整列: 中央;       フォントサイズ: 18px; .paySuccess{ text-align :       center;       マージントップ: 30px;       フォントサイズ: 16px;       色: #f7c58a;
























      フォントの太さ: 太字;
}

pay.json

{       "usingComponents": {},       "navigationBarTextStyle": "black",       "backgroundColor": "#eee",       "navigationBarBackgroundColor": "#f7c58a",       "navigationBarTitleText": "WeChat アプレット支払いデモ" }





伝説

おすすめ

転載: blog.csdn.net/2302_76405773/article/details/130364732