Sfoglia il codice sorgente

add stripe bank card payment method

iamsaltedfish 3 anni fa
parent
commit
4e737c2a37

+ 3 - 2
composer.json

@@ -2,6 +2,7 @@
     "require": {
         "php": "^7.2",
         "ext-curl": "*",
+        "ext-json": "*",
         "ext-mysqli": "*",
         "ext-xml": "*",
         "aws/aws-sdk-php": "3.*",
@@ -22,11 +23,11 @@
         "sentry/sdk": "^2.1",
         "slim/slim": "~3.0",
         "smarty/smarty": "3.*",
+        "stripe/stripe-php": "^7.105",
         "symfony/yaml": "^4.4@dev",
         "telegram-bot/api": "^2.2",
         "voku/anti-xss": "^1.2",
-        "zeuxisoo/slim-whoops": "0.5.*",
-        "ext-json": "*"
+        "zeuxisoo/slim-whoops": "0.5.*"
     },
     "autoload": {
         "psr-4": {

+ 80 - 0
config/settings.json

@@ -848,5 +848,85 @@
         "type": "int",
         "default": "180",
         "mark": "返利时间范围限制(单位:天)"
+    },
+    {
+        "id": null,
+        "item": "stripe_currency",
+        "value": "HKD",
+        "class": "stripe",
+        "is_public": 0,
+        "type": "string",
+        "default": "HKD",
+        "mark": "货币代码"
+    },
+    {
+        "id": null,
+        "item": "stripe_sk",
+        "value": "stripe_sk",
+        "class": "stripe",
+        "is_public": 0,
+        "type": "string",
+        "default": "stripe_sk",
+        "mark": "stripe_sk"
+    },
+    {
+        "id": null,
+        "item": "stripe_pk",
+        "value": "stripe_pk",
+        "class": "stripe",
+        "is_public": 0,
+        "type": "string",
+        "default": "stripe_pk",
+        "mark": "stripe_pk"
+    },
+    {
+        "id": null,
+        "item": "stripe_webhook_key",
+        "value": "stripe_webhook_key",
+        "class": "stripe",
+        "is_public": 0,
+        "type": "string",
+        "default": "stripe_webhook_key",
+        "mark": "web_hook密钥"
+    },
+    {
+        "id": null,
+        "item": "stripe_min_recharge",
+        "value": "10",
+        "class": "stripe",
+        "is_public": 1,
+        "type": "int",
+        "default": "10",
+        "mark": "最低充值限额"
+    },
+    {
+        "id": null,
+        "item": "stripe_card",
+        "value": "0",
+        "class": "stripe",
+        "is_public": 0,
+        "type": "bool",
+        "default": "0",
+        "mark": "银行卡支付"
+    },
+    {
+        "id": null,
+        "item": "stripe_alipay",
+        "value": "0",
+        "class": "stripe",
+        "is_public": 0,
+        "type": "bool",
+        "default": "0",
+        "mark": "支付宝支付"
+    },
+    {
+        "id": null,
+        "item": "stripe_wechat",
+        "value": "0",
+        "class": "stripe",
+        "is_public": 0,
+        "type": "bool",
+        "default": "0",
+        "mark": "微信支付"
     }
 ]

+ 73 - 0
resources/views/material/admin/setting.tpl

@@ -349,6 +349,47 @@
 
                                         <button id="submit_theadpay" type="submit" class="btn btn-block btn-brand">提交</button>
                                     </div>
+
+                                    <div class="tab-pane fade" id="stripe">
+                                        <p class="form-control-guide"><i class="material-icons">warning</i>提供虚拟专用网络业务符合 Stripe 用户协议,但可能不符合 Stripe 提供的部分支付通道(如支付宝、微信)用户协议,相关支付通道可能存在被关闭的风险</p>
+                                        <h5>支付渠道</h5>
+                                        <!-- stripe_card -->
+                                        <div class="form-group form-group-label">
+                                            <label class="floating-label">银行卡支付</label>
+                                            <select id="stripe_card" class="form-control maxwidth-edit">
+                                                <option value="1" {if $settings['stripe_card'] == true}selected{/if}>启用</option>
+                                                <option value="0" {if $settings['stripe_card'] == false}selected{/if}>停用</option>
+                                            </select>
+                                        </div>
+                                        <h5>支付设置</h5>
+                                        <!-- stripe_currency -->
+                                        <div class="form-group form-group-label">
+                                            <label class="floating-label">货币单位</label>
+                                            <input class="form-control maxwidth-edit" id="stripe_currency" value="{$settings['stripe_currency']}">
+                                        </div>
+                                        <!-- stripe_min_recharge -->
+                                        <div class="form-group form-group-label">
+                                            <label class="floating-label">最低充值限额(整数)</label>
+                                            <input class="form-control maxwidth-edit" id="stripe_min_recharge" value="{$settings['stripe_min_recharge']}">
+                                        </div>
+                                        <!-- stripe_pk -->
+                                        <div class="form-group form-group-label">
+                                            <label class="floating-label">stripe_pk</label>
+                                            <input class="form-control maxwidth-edit" id="stripe_pk" value="{$settings['stripe_pk']}">
+                                        </div>
+                                        <!-- stripe_sk -->
+                                        <div class="form-group form-group-label">
+                                            <label class="floating-label">stripe_sk</label>
+                                            <input class="form-control maxwidth-edit" id="stripe_sk" value="{$settings['stripe_sk']}">
+                                        </div>
+                                        <!-- stripe_webhook_key -->
+                                        <div class="form-group form-group-label">
+                                            <label class="floating-label">WebHook密钥</label>
+                                            <input class="form-control maxwidth-edit" id="stripe_webhook_key" value="{$settings['stripe_webhook_key']}">
+                                        </div>
+
+                                        <button id="submit_stripe" type="submit" class="btn btn-block btn-brand">提交</button>
+                                    </div>
                                     
                                     <div class="tab-pane fade" id="vmqpay">
                                         <p class="form-control-guide"><i class="material-icons">info</i>此支付方式需自建网关并配置各项参数。访问 <a href="https://github.com/szvone/vmqphp" target="view_window">https://github.com/szvone/vmqphp</a> 了解更多</p>
@@ -1386,6 +1427,38 @@
     })
 </script>
 
+<script>
+    window.addEventListener('load', () => {
+        $$.getElementById('submit_stripe').addEventListener('click', () => {
+            $.ajax({
+                type: "POST",
+                url: "/admin/setting",
+                dataType: "json",
+                data: {
+                    class: 'stripe',
+                    stripe_card: $$getValue('stripe_card'),
+                    stripe_currency: $$getValue('stripe_currency'),
+                    stripe_pk: $$getValue('stripe_pk'),
+                    stripe_sk: $$getValue('stripe_sk'),
+                    stripe_webhook_key: $$getValue('stripe_webhook_key')
+                },
+                success: data => {
+                    $("#result").modal();
+                    $$.getElementById('msg').innerHTML = data.msg;
+                    if (data.ret) {
+                        window.setTimeout("location.href='/admin/setting'", {$config['jump_delay']});
+                    }
+                },
+                error: jqXHR => {
+                    alert(`发生错误:${
+                            jqXHR.status
+                            }`);
+                }
+            })
+        })
+    })
+</script>
+
 <script>
     window.addEventListener('load', () => {
         $$.getElementById('submit_coinpay').addEventListener('click', () => {

+ 11 - 0
resources/views/material/user/stripe_card.tpl

@@ -0,0 +1,11 @@
+<div class="card-inner">
+    <p class="card-heading">账户充值</p>
+    <p>可以使用带有银联、MasterCard、Visa 等标识的信用卡或借记卡</p>
+    <form action="/user/payment/purchase/stripe_card" method="post">
+        <div class="form-group form-group-label">    
+            <label class="floating-label" for="amount-stripe-card">金额</label>
+            <input class="form-control" id="price" name="price" min="{$config['stripe_min_recharge']}" step="0.1" type="number" required="required">
+            <button class="btn btn-flat waves-attach" type="submit"><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAABmJLR0QA/wD/AP+gvaeTAAAEYElEQVRoge1YTW8cRRB93ev1rokUPqLwIS8IgZSQgDgAEuIajkj8DB848ysQZ0T+CLlxQ1yRIIGTZclLLGOwoyT2st51PQ4z3dM9XT07Y6/NZUu2PNvVH+9VvarpNbCyla1sZf+nmfrAxz8efiFi7gPYBOh+CmPwh340fAzmszY/mBIuSPYnWOw5tgZbj7589UETgbX6gIj5HuCm3zg6OGaREqMyv/qgB6IaDOcbYnRG3AfwVicCIEdXB5zK/GorQ77ZBF4lwOgQRSatQDshaKDL1RnQzlEPYkcCVx/trsCzBPLRaVGUS4o261HoQoAMgSyvKNsDV7LfhYDX7wVaoEpaAf31aIK3hxIdvz2x+HZ32FpGSgZyWl2+TOrgAeCdDanWczGLbBdyH5ZdlF4mDdjqNdVkeQmpoIuHy9Z2i8B7SzOQ1f8SgS9kEGBZYI1d6MItUAEdant7YgvNB7Y9MVEQf/v0Hl9c6+Oa7Y0h3HrlpwfR3UiV0GVEW2vJ3+wMgvkZ2RKAEDAcEUzuRtkulAWuElscbX1tU62VWISgJSgCEMndKFPENdDJxvGpywUeZ58Ul4E4uDkC4eGdgZ/3JVd+UAMhAK2AYtT2pEvonEWZguoWbZ20AGIBiNqTMkVcRfvWjQHW+2uYSw8zsZhLDyenZ9h/eoTbN4cY1HzHp3PsHz3Be68NMFjvx77pHHuHR7j7xjDxPZ/O8fjg0AMIryCkgGydgSoURPGd04LFryl+YQqnceOhD0UW1XWLfLVOBaAoXmMBaZ2BWIt/HEzVFggAv+9PAU5VmTzc+9dvUpfJr39OIi01vnlZdCDaThlwz5dQlJm1WfxlB4KISlTpQmYMykgHdZGi1Dtbk9189jcMBRQLGqoLbUpAtkCMSVTXChY92Heo0pf6ofhZ+oP5LcF/9cN3WKNBz+0h6eLk/0J1e/jZ5z7f9S/2Drh7rnewKgVuC8cCnpRnWxszBPowGFqDISx65ZGvP/o5wqx8J47tul1LQBd/KnAOvC/2gGlEEKjeqCYgHb5lTbWvhUEfKDIQ7tGFwDXb84tJd0DZYA2L/gzXWotnGlPNKQnSuGfj9yBMtC7er9xDXGDSFtuKgJeJj3Y5hoaxnExCKbqC8GuRSsyPBXt0JVDeAv1hMcgQTAUgPbiUXI0ga4DVMVWaHQjkM1ABrua5OVq0u5IO99PBtyJAoT8YBK7ffhf99QFwOgNmM+B0htnJBE/2/8JL799CfzCMfccnOHq8h5c/vIP+cCPynT47xuHuGDc++gD9jRdi39Pn+GdnJ8ieTmJxBtwdxEfbANYAPQucWcDa0ieqj+VVQF2HYh0zvqLv68XrbOF74OCTe4xlEuuzU1EmkkjHmvQOAJs7v3R7D8QZcIfp+swXYFNRhkWeB56zxTVAjkGO2kU7IHge0gvN7NZHkrtQYnNsUTgmpdCkSAFG3BeNYKyc48ckGJOyTqS8Hrva6ADewGy1nLyyla1sZVdk/wH7Rlk322CefQAAAABJRU5ErkJggg=="/></button>
+        </div>
+    </form>
+</div>

+ 4 - 0
src/Controllers/Admin/SettingController.php

@@ -64,6 +64,9 @@ class SettingController extends AdminController
             case 'paymentwall':
                 $list = array('pmw_publickey', 'pmw_privatekey', 'pmw_widget', 'pmw_height');
                 break;
+            case 'stripe':
+                $list = array('stripe_card', 'stripe_currency', 'stripe_pk', 'stripe_sk', 'stripe_webhook_key');
+                break;
             // 邮件
             case 'mail':
                 $list = array('mail_driver');
@@ -178,6 +181,7 @@ class SettingController extends AdminController
             "当面付" => "f2fpay",
             "PayJs" => "payjs",
             "PaymentWall" => "paymentwall",
+            "Stripe" => "stripe",
             "TheadPay" => "theadpay",
             "V免签" => "vmqpay"
         );

+ 2 - 0
src/Services/Config.php

@@ -32,6 +32,8 @@ class Config
             'version'                 => VERSION,
             'appName'                 => $_ENV['appName'],
             'baseUrl'                 => $_ENV['baseUrl'],
+            // 充值
+            'stripe_min_recharge'     => $public_configs['stripe_min_recharge'],
             // 个性化
             'user_center_bg'          => $public_configs['user_center_bg'],
             'admin_center_bg'         => $public_configs['admin_center_bg'],

+ 115 - 0
src/Services/Gateway/StripeCard.php

@@ -0,0 +1,115 @@
+<?php
+namespace App\Services\Gateway;
+
+use App\Services\View;
+use App\Services\Auth;
+use App\Models\Paylist;
+use App\Models\Setting;
+use Stripe\Stripe;
+
+class StripeCard extends AbstractPayment
+{
+    public static function _name()
+    {
+        return 'stripe_card';
+    }
+
+    public static function _enable()
+    {
+        if (self::getActiveGateway('stripe') && Setting::obtain('stripe_card')) {
+            return true;
+        }
+
+        return false;
+    }
+
+    public function purchase($request, $response, $args)
+    {
+        $trade_no = uniqid();
+        $user     = Auth::getUser();
+        $configs  = Setting::getClass('stripe');
+        $price    = $request->getParam('price');
+
+        $pl          = new Paylist();
+        $pl->userid  = $user->id;
+        $pl->total   = $price;
+        $pl->tradeno = $trade_no;
+        $pl->save();
+
+        $params = [
+            'trade_no' => $trade_no,
+            'sign'     => md5($trade_no . ':' . $configs['stripe_webhook_key'])
+        ];
+        
+        $exchange_amount = ($price / self::exchange($configs['stripe_currency'])) * 100;
+        
+        \Stripe\Stripe::setApiKey($configs['stripe_sk']);
+        $session = \Stripe\Checkout\Session::create([
+            'customer_email' => $user->email,
+            'line_items' => [[
+              'price_data' => [
+                'currency' => $configs['stripe_currency'],
+                'product_data' => [
+                  'name' => 'Account Recharge',
+                ],
+                'unit_amount' => (int) $exchange_amount,
+              ],
+              'quantity' => 1,
+            ]],
+            'mode' => 'payment',
+            'success_url' => self::getUserReturnUrl() . '?session_id={CHECKOUT_SESSION_ID}&' . http_build_query($params),
+            'cancel_url' => $_ENV['baseUrl'] . '/user/code',
+        ]);
+        
+        header('Location: ' . $session->url);
+    }
+	
+    public function notify($request, $response, $args)
+    {
+        return;
+    }
+	
+    public static function getPurchaseHTML()
+    {
+        return View::getSmarty()->fetch('user/stripe_card.tpl');
+    }
+	
+    public function getReturnHTML($request, $response, $args)
+    {
+        $sign      = $request->getParam('sign');
+        $trade_no   = $request->getParam('trade_no');
+        $session_id = $request->getParam('session_id');
+
+        $_sign = md5($trade_no . ':' . Setting::obtain('stripe_webhook_key'));
+        if ($_sign != $sign) {
+            die('error_sign');
+        }
+        
+        $stripe = new \Stripe\StripeClient(Setting::obtain('stripe_sk'));
+        $session = $stripe->checkout->sessions->retrieve($session_id, []);
+
+        if ($session->payment_status == 'paid') {
+            $this->postPayment($trade_no, '银行卡支付');
+        }
+
+        header('Location: ' . $_ENV['baseUrl'] . '/user/code');
+    }
+
+    public function getStatus($request, $response, $args)
+    {
+        // TODO: Implement getStatus() method.
+    }
+
+    public static function exchange($currency)
+    {
+        $ch = curl_init();
+        $url = 'https://api.exchangerate.host/latest?symbols=CNY&base=' . strtoupper($currency);
+        curl_setopt($ch, CURLOPT_URL, $url);
+        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
+        curl_setopt($ch, CURLOPT_HEADER, 0);
+        $currency = json_decode(curl_exec($ch));
+        curl_close($ch);
+
+        return $currency->rates->CNY;
+    }
+}