首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 綜合知識

PHP 微信公眾號發送紅包 API 完整封裝類

     編輯:PHP 綜合知識

接口背景

微信企業付款到零錢(紅包)接口:https://api.mch.weixin.qq.com/mmpaymkttransfers/sendredpack,需雙向證書認證、XML 簽名、POST 請求。

核心類:WechatRedPacket

<?php
class WechatRedPacket {
    // 接口配置
    private $url = 'https://api.mch.weixin.qq.com/mmpaymkttransfers/sendredpack';
    
    // 商戶配置(建議從配置文件讀取)
    private $mch_id;
    private $appid;
    private $send_name;
    private $total_num = 1;
    private $wishing;
    private $act_name;
    private $remark;
    private $api_key;       // API 密鑰(簽名用)
    private $cert_path;     // 證書路徑
    private $key_path;      // 私鑰路徑
    private $rootca_path;   // CA 證書路徑
    
    // 運行時變量
    private $nonce_str;
    private $client_ip;

    public function __construct(array $config) {
        $this->mch_id      = $config['mch_id'];
        $this->appid       = $config['appid'];
        $this->send_name   = $config['send_name'];
        $this->wishing     = $config['wishing'];
        $this->act_name    = $config['act_name'];
        $this->remark      = $config['remark'];
        $this->api_key     = $config['api_key'];
        $this->cert_path   = $config['cert_path'];
        $this->key_path    = $config['key_path'];
        $this->rootca_path = $config['rootca_path'];
        $this->client_ip   = $_SERVER['SERVER_ADDR'] ?? '127.0.0.1';
        $this->nonce_str   = $this->generateNonceStr(32);
    }

    // 發送紅包主入口
    public function send(string $mch_billno, string $openid, int $total_amount): array {
        $sign = $this->makeSign(compact('mch_billno', 'openid', 'total_amount'));
        
        $data = [
            'nonce_str'   => $this->nonce_str,
            'mch_billno'  => $mch_billno,      // 商戶訂單號:mch_id + ymd + 10位流水
            'mch_id'      => $this->mch_id,
            'wxappid'     => $this->appid,
            'send_name'   => $this->send_name,
            're_openid'   => $openid,          // 用戶 openid
            'total_amount'=> $total_amount,    // 金額(分)
            'total_num'   => $this->total_num,
            'wishing'     => $this->wishing,
            'client_ip'   => $this->client_ip,
            'act_name'    => $this->act_name,
            'remark'      => $this->remark,
            'sign'        => $sign,
        ];
        
        $xml = $this->arrayToXml($data);
        $response = $this->curlPostSsl($this->url, $xml);
        return $this->xmlToArray($response);
    }

    // 生成簽名(按鍵名 ASCII 排序拼接 + key)
    private function makeSign(array $params): string {
        // 過濾空值並按鍵名排序
        $params = array_filter($params, fn($v) => $v !== '');
        ksort($params);
        
        $stringA = '';
        foreach ($params as $k => $v) {
            $stringA .= "{$k}={$v}&";
        }
        $stringSignTemp = $stringA . "key={$this->api_key}";
        
        return strtoupper(md5($stringSignTemp));
    }

    // 數組轉 XML(支持 CDATA)
    private function arrayToXml(array $data): string {
        $xml = "";
        foreach ($data as $k => $v) {
            if (is_numeric($v)) {
                $xml .= "<{$k}>{$v}";
            } else {
                $xml .= "<{$k}>";
            }
        }
        $xml .= "";
        return $xml;
    }

    // cURL 雙向證書請求
    private function curlPostSsl(string $url, string $xml, int $timeout = 30): string {
        $ch = curl_init();
        curl_setopt_array($ch, [
            CURLOPT_URL            => $url,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_TIMEOUT        => $timeout,
            CURLOPT_POST           => true,
            CURLOPT_POSTFIELDS     => $xml,
            CURLOPT_SSL_VERIFYPEER => false, // 生產環境建議 true 並配置 CA
            CURLOPT_SSL_VERIFYHOST => false,
            CURLOPT_SSLCERTTYPE    => 'PEM',
            CURLOPT_SSLCERT        => $this->cert_path,
            CURLOPT_SSLKEYTYPE     => 'PEM',
            CURLOPT_SSLKEY         => $this->key_path,
            CURLOPT_CAINFO         => $this->rootca_path,
        ]);
        
        $response = curl_exec($ch);
        if ($response === false) {
            throw new \RuntimeException("cURL Error: " . curl_error($ch));
        }
        curl_close($ch);
        return $response;
    }

    // XML 轉數組
    private function xmlToArray(string $xml): array {
        libxml_disable_entity_loader(true);
        return json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
    }

    // 生成隨機字符串
    private function generateNonceStr(int $length = 32): string {
        $chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz';
        $str = '';
        for ($i = 0; $i < $length; $i++) {
            $str .= $chars[random_int(0, strlen($chars) - 1)];
        }
        return $str;
    }
}

使用示例

$config = [
    'mch_id'      => '1234567890',
    'appid'       => 'wx8888888888888888',
    'send_name'   => '測試商戶',
    'wishing'     => '恭喜發財',
    'act_name'    => '新年紅包',
    'remark'      => '測試發送',
    'api_key'     => 'your_api_key_32_chars',
    'cert_path'   => '/path/to/apiclient_cert.pem',
    'key_path'    => '/path/to/apiclient_key.pem',
    'rootca_path' => '/path/to/rootca.pem',
];

$redPacket = new WechatRedPacket($config);

// 商戶訂單號格式:mch_id + Ymd + 10位流水
$mch_billno = $config['mch_id'] . date('Ymd') . str_pad(random_int(1, 9999999999), 10, '0', STR_PAD_LEFT);
$openid     = 'oUpF8uMuAJO_M2pxb1Q9zNjWeSs0'; // 用戶 openid
$amount     = 100; // 1.00 元 = 100 分

try {
    $result = $redPacket->send($mch_billno, $openid, $amount);
    print_r($result);
    /* 成功返回示例:
    Array (
        [return_code] => SUCCESS
        [result_code] => SUCCESS
        [mch_billno] => 1234567890202401150000000001
        [send_listid] => 1000000000202401150000000001
    )
    */
} catch (\Exception $e) {
    echo "發送失敗: " . $e->getMessage();
}

關鍵注意事項

項目要求
libcurl 版本≥ 7.20.1 支持雙向證書
證書格式PEM 格式,含申請商戶號時下載的證書
金額單位分(整數),1 元 = 100 分
商戶訂單號唯一,格式:mch_id + Ymd + 10位數字
IP 白名單商戶平台需配置調用服務器 IP
單次限額普通紅包 ≤ 200 元,裂變紅包 ≤ 500 元

錯誤碼速查

  • SEND_FAILED:系統錯誤,重試
  • NOTENOUGH:餘額不足
  • PARAM_ERROR:參數錯誤(檢查簽名、金額、openid)
  • OPENID_ERROR:openid 不屬於該公眾號
  • SIGN_ERROR:簽名驗證失敗(檢查 API Key、參數排序)

此封裝類結構清晰、異常處理完善,可直接集成到 Laravel、ThinkPHP、Yii 等框架的 Service 層中。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved