PayStripeApi.php 8.5 KB
<?php
/**
 * @remark :
 * @name   :PayStripeApi.php
 * @author :lyh
 * @method :post
 * @time   :2024/12/24 10:35
 */

namespace App\Helper;

use App\Models\ExtentModule\ExtensionModuleValue;
use App\Services\ProjectServer;
use Illuminate\Support\Facades\DB;

class PayStripeApi
{
    private $secretKey;
    //币种对应支付方式
    public $currency_types = [
        'usd' => ['card'],
//        'eur' => ['card', 'ideal', 'giropay', 'sofort', 'bancontact', 'klarna', 'link'],
//        'gbp' => ['card', 'apple_pay', 'google_pay', 'klarna', 'link', 'afterpay_clearpay'],
//        'aud' => ['card', 'afterpay_clearpay', 'apple_pay', 'google_pay'],
//        'cad' => ['card', 'apple_pay', 'google_pay', 'link'],
//        'sgd' => ['card', 'grabpay', 'fpx', 'google_pay'],
//        'jpy' => ['card', 'apple_pay', 'google_pay'],
        'cny' => ['alipay', 'wechat_pay'],
//        'brl' => ['card', 'boleto', 'pix'],
//        'mxn' => ['card', 'oxxo'],
//        'inr' => ['card', 'upi', 'netbanking'],
//        'php' => ['card', 'paymaya', 'gcash'],
//        'myr' => ['card', 'fpx'],
//        'thb' => ['card', 'promptpay'],
//        'idr' => ['card', 'bank_transfer'],
//        'zar' => ['card'],
//        'ngn' => ['card'],
//        'aed' => ['card', 'apple_pay', 'google_pay']
    ];

    // 构造函数设置密钥
    public function __construct()
    {
        $this->secretKey = 'sk_test_51MyseXIWCYVeLww1tbPZzRe1Qk4lS5d2VLiDjpju7G0ToiX1RJcFinQXNlftq9eCjZE0n2gjaz1mfy1g0mxTusdf00m636Gv62';
    }

    /**
     * @remark :通用的 cURL 请求方法
     * @name   :sendRequest
     * @author :lyh
     * @method :post
     * @time   :2024/12/24 10:38
     */
    private function sendRequest($url, $method = 'POST', $data = [])
    {
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_HTTPHEADER, [
            "Authorization: Bearer {$this->secretKey}",
            "Content-Type: application/x-www-form-urlencoded"
        ]);
        if ($method === 'POST') {
            curl_setopt($ch, CURLOPT_POST, true);
            curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data));
        } elseif ($method === 'GET') {
            curl_setopt($ch, CURLOPT_HTTPGET, true);
        } elseif ($method === 'DELETE') {
            curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
        }
        $response = curl_exec($ch);
        if (curl_errno($ch)) {
            throw new Exception('cURL Error: ' . curl_error($ch));
        }
        curl_close($ch);
        return json_decode($response, true);
    }

    /**
     * @remark :创建支付意图
     * @name   :createPaymentIntent
     * @author :lyh
     * @method :post
     * @time   :2024/12/24 10:38
     */
    public function createPaymentIntent($amount, $currency = 'usd')
    {
        $url = "https://api.stripe.com/v1/payment_intents";
        $data = [
            'amount' => $amount,
            'currency' => $currency,
            'automatic_payment_methods[enabled]' => 'true',
        ];
        return $this->sendRequest($url, 'POST', $data);
    }

    /**
     * @remark :查询支付意图
     * @name   :retrievePaymentIntent
     * @author :lyh
     * @method :post
     * @time   :2024/12/24 10:38
     */
    public function retrievePaymentIntent($paymentIntentId)
    {
        $url = "https://api.stripe.com/v1/payment_intents/{$paymentIntentId}";
        return $this->sendRequest($url, 'GET');
    }

    /**
     * @remark :创建退款
     * @name   :createRefund
     * @author :lyh
     * @method :post
     * @time   :2024/12/24 10:42
     */
    public function createRefund($chargeId, $amount = null)
    {
        $url = "https://api.stripe.com/v1/refunds";
        $data = ['charge' => $chargeId];
        if ($amount) {
            $data['amount'] = $amount;
        }
        return $this->sendRequest($url, 'POST', $data);
    }

    /**
     * @remark :查询退款
     * @name   :retrieveRefund
     * @author :lyh
     * @method :post
     * @time   :2024/12/24 10:42
     */
    public function retrieveRefund($refundId)
    {
        $url = "https://api.stripe.com/v1/refunds/{$refundId}";
        return $this->sendRequest($url, 'GET');
    }

    /**
     * @remark :处理 Webhook
     * @name   :handleWebhook
     * @author :lyh
     * @method :post
     * @time   :2024/12/24 10:43
     */
    public static function handleWebhook()
    {
        try {
            // Webhook 签名密钥(从 Stripe 仪表盘获取)
            $endpointSecret = 'whsec_garhW2TrCIrduyM3rve9mFS2sn69B9Yt';
            // 获取原始请求内容
            $payload = request()->getContent();
            // 获取 Stripe 签名头
            $sigHeader = request()->header('Stripe-Signature');
            // 验证签名
            if (!self::verifySignature($payload, $sigHeader, $endpointSecret)) {
                return [
                    'code' => '201',
                    'message' => 'Invalid signature',
                    'details' => [
                        'payload' => $payload,
                        'sigHeader' => $sigHeader,
                        'endpointSecret' => $endpointSecret,
                    ],
                ]; // 返回 400 Bad Request 状态码;
            }
            $event = json_decode($payload, true);
            // 获取事件类型
            $eventType = $event['type'];
            $eventData = $event['data']['object'];
            // 根据事件类型处理
            switch ($eventType) {
                case 'payment_intent.succeeded':
                    // 处理支付成功逻辑
                    $paymentIntentId = $eventData['id'];
                    self::getExtensionInfo($paymentIntentId,$eventData);
                    break;
                case 'payment_intent.payment_failed':
                    // 处理支付失败逻辑
                    $error = $eventData['last_payment_error'];
                    break;
                case 'charge.refunded':
                    // 处理退款逻辑
                    $chargeId = $eventData['id'];
                    break;
                default:
                    throw new \Exception('Unhandled event type: ' . $eventType);
            }
            return $event;
        } catch (Exception $e) {
            throw new \Exception('Webhook Error: ' . $e->getMessage());
        }
    }

    /**
     * @remark :根据id获取当前数据详情
     * @name   :getExtensionInfo
     * @author :lyh
     * @method :post
     * @time   :2024/12/25 14:43
     */
    public static function getExtensionInfo($id,$eventData){
        @file_put_contents(storage_path('logs/lyh_3059_error.log'), var_export('进入', true) . PHP_EOL, FILE_APPEND);
        ProjectServer::useProject(3059);
        $extensionModel = new ExtensionModuleValue();
        $info = $extensionModel->read(['value'=>$id]);
        if($info === false){
            @file_put_contents(storage_path('logs/lyh_3059_error.log'), var_export($id.':当前数据错误', true) . PHP_EOL, FILE_APPEND);
        }
        //组装数据保存
        $data = [
            ['uuid'=>$info['uuid'],'module_id'=>$info['module_id'],'field_id'=>6,'value'=>'success'],
            ['uuid'=>$info['uuid'],'module_id'=>$info['module_id'],'field_id'=>7,'value'=>json_encode($eventData)],
        ];
        $rs = $extensionModel->insertAll($data);
        @file_put_contents(storage_path('logs/lyh_3059_error.log'), var_export('结束'.$rs, true) . PHP_EOL, FILE_APPEND);
        DB::disconnect('custom_mysql');
    }

    /**
     * @remark :验证签名
     * @name   :verifySignature
     * @author :lyh
     * @method :post
     * @time   :2024/12/24 15:55
     */
    public static function verifySignature($payload, $sigHeader, $endpointSecret)
    {
        // 解析 Signature Header,获取 timestamp 和签名
        if (!preg_match('/t=(\d+),v1=([a-f0-9]+)/', $sigHeader, $matches)) {
            return false; // 签名格式错误
        }
        $timestamp = $matches[1]; // 提取时间戳
        $receivedSignature = $matches[2]; // 提取签名
        // 防止重放攻击:检查时间戳是否在 5 分钟以内
        $currentTimestamp = time();
        if (abs($currentTimestamp - $timestamp) > 300) {
            return false; // 签名过期
        }
        // 计算预期签名
        $signedPayload = "{$timestamp}.{$payload}";
        $expectedSignature = hash_hmac('sha256', $signedPayload, $endpointSecret);
        // 比较签名是否匹配
        return hash_equals($expectedSignature, $receivedSignature);
    }


}