Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[update] 新增stripe聚合支付方式,采用全新的paymentIntents API #136

Open
wants to merge 24 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
2614874
[update] 新增stripe聚合支付方式,采用全新的paymentIntents API
linusxiong Jun 4, 2024
5cc0b77
Merge branch 'cedar2025:dev' into dev
linusxiong Jun 13, 2024
9270d94
Update StripeALLInOne.php
linusxiong Jun 13, 2024
fc283af
Update StripeALLInOne.php
linusxiong Jun 13, 2024
338aad7
Fix PaymentController.php
linusxiong Jun 13, 2024
0389edd
Merge branch 'cedar2025:dev' into dev
linusxiong Jun 14, 2024
bab7ed8
[fix] 修复catch的时候变量可能undefined的问题
linusxiong Jun 14, 2024
8cc247b
[feat] 新增telegram机器人/start指令
linusxiong Jun 14, 2024
f0c620c
[fix] 删除重复包导入
linusxiong Jun 17, 2024
acb40cc
[update] 新增credit card跳转至checkout页面付款
linusxiong Jul 2, 2024
b0fcc92
Merge branch 'cedar2025:dev' into dev
linusxiong Jul 19, 2024
190e64b
Merge branch 'cedar2025:dev' into dev
linusxiong Sep 16, 2024
1d36069
Merge branch 'cedar2025:dev' into dev
linusxiong Oct 16, 2024
6cfdd1c
Update Surge.php
linusxiong Oct 18, 2024
e482b72
Merge branch 'cedar2025:dev' into dev
linusxiong Nov 14, 2024
1d9cb22
Merge branch 'cedar2025:dev' into dev
linusxiong Dec 2, 2024
c4595bc
use docker env on .env file
linusxiong Dec 8, 2024
1378fdb
fix bugs
linusxiong Dec 8, 2024
cfc8a05
fix bugs
linusxiong Dec 8, 2024
dee7525
Update Dockerfile
linusxiong Dec 8, 2024
22ffe0d
Update Dockerfile
linusxiong Dec 8, 2024
aa3ff5c
fix bug
linusxiong Dec 8, 2024
654f1f8
Update supervisord.conf
linusxiong Dec 8, 2024
256ca28
Merge branch 'cedar2025:dev' into dev
linusxiong Dec 31, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .docker/etc/supervisor/supervisord.conf
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ directory=/www
command=sh -c "chown -R www:www /www && chmod -R 775 /www"
autostart=true
autorestart=false
priority=1
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
Expand All @@ -25,6 +26,7 @@ stderr_logfile_maxbytes=0
[program:nginx]
command=nginx -g 'daemon off;'
user=root
priority=5
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
Expand All @@ -36,6 +38,7 @@ startretries=10
[program:cron]
command=crond -f -l 8
user=root
priority=4
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
Expand All @@ -62,6 +65,8 @@ command=php -c php.ini webman.php start
directory=/www
user=www
numprocs=1
priority=2
startsecs=3
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
Expand All @@ -74,6 +79,7 @@ startretries=10
command=php artisan horizon
directory=/www
user=www
priority=3
stdout_logfile=/www/storage/logs/queue.log
stdout_logfile_maxbytes=0
stderr_logfile=/www/storage/logs/queue_error.log
Expand Down
2 changes: 1 addition & 1 deletion .env.example
100755 → 100644
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,4 @@ GOOGLE_CLOUD_KEY_FILE=config/googleCloudStorageKey.json
GOOGLE_CLOUD_STORAGE_BUCKET=

# 用于阻止重复安装
INSTALLED=false
INSTALLED=false
3 changes: 2 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ COPY .docker /
COPY . /www
RUN composer install --optimize-autoloader --no-cache --no-dev \
&& php artisan storage:link \
&& cp /www/.env.example /www/.env \
&& chown -R www:www /www \
&& chmod -R 775 /www

CMD /usr/bin/supervisord --nodaemon -c /etc/supervisor/supervisord.conf
CMD ["/usr/bin/supervisord", "--nodaemon", "-c", "/etc/supervisor/supervisord.conf"]
5 changes: 3 additions & 2 deletions app/Console/Commands/BackupDatabase.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,10 @@ public function handle()
}

// 数据库备份逻辑
$databaseBackupPath = storage_path('backup/' . now()->format('Y-m-d_H-i-s') . '_' . config('database.connections.mysql.database') . '_database_backup.sql');
$compressedBackupPath = $databaseBackupPath . '.gz';
try{
if (config('database.default') === 'mysql'){
$databaseBackupPath = storage_path('backup/' . now()->format('Y-m-d_H-i-s') . '_' . config('database.connections.mysql.database') . '_database_backup.sql');
$this->info("1️⃣:开始备份Mysql");
\Spatie\DbDumper\Databases\MySql::create()
->setHost(config('database.connections.mysql.host'))
Expand Down Expand Up @@ -83,7 +84,7 @@ public function handle()
$bucket->upload(fopen($compressedBackupPath, 'r'), [
'name' => $objectName,
]);

// 输出文件链接
\Log::channel('backup')->info("🎉:数据库备份已上传到 Google Cloud Storage: $objectName");
$this->info("🎉:数据库备份已上传到 Google Cloud Storage: $objectName");
Expand Down
2 changes: 1 addition & 1 deletion app/Http/Controllers/V1/Guest/PaymentController.php
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ private function handle($tradeNo, $callbackNo)
$payment->name,
$order->trade_no
);

$telegramService->sendMessageWithAdmin($message);
return true;
}
Expand Down
5 changes: 2 additions & 3 deletions app/Payments/BTCPay.php
Original file line number Diff line number Diff line change
Expand Up @@ -76,14 +76,13 @@ public function notify($params)
//NOT BTCPay-Sig
//API doc is WRONG!
$headerName = 'Btcpay-Sig';
$signraturHeader = isset($headers[$headerName]) ? $headers[$headerName] : '';
$signatureHeader = isset($headers[$headerName]) ? $headers[$headerName] : '';
$json_param = json_decode($payload, true);

$computedSignature = "sha256=" . \hash_hmac('sha256', $payload, $this->config['btcpay_webhook_key']);

if (!self::hashEqual($signraturHeader, $computedSignature)) {
if (!self::hashEqual($signatureHeader, $computedSignature)) {
throw new ApiException('HMAC signature does not match', 400);
return false;
}

//get order id store in metadata
Expand Down
210 changes: 210 additions & 0 deletions app/Payments/StripeALLInOne.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
<?php

/**
* 自己写别抄,抄NMB抄
*/
namespace App\Payments;
use App\Exceptions\ApiException;

class StripeALLInOne {
public function __construct($config)
{
$this->config = $config;
}

public function form()
{
return [
'currency' => [
'label' => '货币单位',
'description' => '请使用符合ISO 4217标准的三位字母,例如GBP',
'type' => 'input',
],
'stripe_sk_live' => [
'label' => 'SK_LIVE',
'description' => '',
'type' => 'input',
],
'stripe_webhook_key' => [
'label' => 'WebHook密钥签名',
'description' => 'whsec_....',
'type' => 'input',
],
'description' => [
'label' => '自定义商品介绍',
'description' => '',
'type' => 'input',
],
'payment_method' => [
'label' => '支付方式',
'description' => '请输入alipay, wechat_pay, cards',
'type' => 'input',
]
];
}

public function pay($order)
{
$currency = $this->config['currency'];
$exchange = $this->exchange('CNY', strtoupper($currency));
if (!$exchange) {
throw new ApiException('Currency conversion has timed out, please try again later', 500);
}
//jump url
$jumpUrl = null;
$actionType = 0;
$stripe = new \Stripe\StripeClient($this->config['stripe_sk_live']);

if ($this->config['payment_method'] != "cards"){
$stripePaymentMethod = $stripe->paymentMethods->create([
'type' => $this->config['payment_method'],
]);
// 准备支付意图的基础参数
$params = [
'amount' => floor($order['total_amount'] * $exchange),
'currency' => $currency,
'confirm' => true,
'payment_method' => $stripePaymentMethod->id,
'automatic_payment_methods' => ['enabled' => true],
'statement_descriptor' => 'sub-' . $order['user_id'] . '-' . substr($order['trade_no'], -8),
'description' => $this->config['description'],
'metadata' => [
'user_id' => $order['user_id'],
'out_trade_no' => $order['trade_no'],
'identifier' => ''
],
'return_url' => $order['return_url']
];

// 如果支付方式为 wechat_pay,添加相应的支付方式选项
if ($this->config['payment_method'] === 'wechat_pay') {
$params['payment_method_options'] = [
'wechat_pay' => [
'client' => 'web'
],
];
}
//更新支持最新的paymentIntents方法,Sources API将在今年被彻底替
$stripeIntents = $stripe->paymentIntents->create($params);

$nextAction = null;

if (!$stripeIntents['next_action']) {
throw new ApiException(__('Payment gateway request failed'));
}else {
$nextAction = $stripeIntents['next_action'];
}

switch ($this->config['payment_method']){
case "alipay":
if (isset($nextAction['alipay_handle_redirect'])){
$jumpUrl = $nextAction['alipay_handle_redirect']['url'];
$actionType = 1;
}else {
throw new ApiException('unable get Alipay redirect url', 500);
}
break;
case "wechat_pay":
if (isset($nextAction['wechat_pay_display_qr_code'])){
$jumpUrl = $nextAction['wechat_pay_display_qr_code']['data'];
}else {
throw new ApiException('unable get WeChat Pay redirect url', 500);
}
}
} else {
$creditCheckOut = $stripe->checkout->sessions->create([
'success_url' => $order['return_url'],
'client_reference_id' => $order['trade_no'],
'payment_method_types' => ['card'],
'line_items' => [
[
'price_data' => [
'currency' => $currency,
'unit_amount' => floor($order['total_amount'] * $exchange),
'product_data' => [
'name' => 'sub-' . $order['user_id'] . '-' . substr($order['trade_no'], -8),
'description' => $this->config['description'],
]
],
'quantity' => 1,
],
],
'mode' => 'payment',
]);
$jumpUrl = $creditCheckOut['url'];
$actionType = 1;
}

return [
'type' => $actionType,
'data' => $jumpUrl
];
}

public function notify($params)
{
try {
\Stripe\Stripe::setApiKey($this->config['stripe_sk_live']);
//Workerman不支持使用php://input, stripe同时要求验证签名的payload不能经过修改,所以使用这个方法
$payload = $GLOBALS['HTTP_RAW_POST_DATA'];
$headers = getallheaders();
$headerName = 'Stripe-Signature';
$signatureHeader = $headers[$headerName] ?? '';
$event = \Stripe\Webhook::constructEvent(
$payload,
$signatureHeader,
$this->config['stripe_webhook_key']
);

} catch (\UnexpectedValueException $e){
throw new ApiException('Error parsing payload', 400);
}
catch (\Stripe\Exception\SignatureVerificationException $e) {
throw new ApiException('signature not match', 400);
}
switch ($event->type) {
case 'payment_intent.succeeded':
$object = $event->data->object;
if ($object->status === 'succeeded') {
if (!isset($object->metadata->out_trade_no)) {
return('order error');
}
$metaData = $object->metadata;
$tradeNo = $metaData->out_trade_no;
return [
'trade_no' => $tradeNo,
'callback_no' => $object->id
];
}
break;
case 'checkout.session.completed':
$object = $event->data->object;
if ($object->payment_status === 'paid') {
return [
'trade_no' => $object->client_reference_id,
'callback_no' => $object->payment_intent
];
}
break;
case 'checkout.session.async_payment_succeeded':
$object = $event->data->object;
return [
'trade_no' => $object->client_reference_id,
'callback_no' => $object->payment_intent
];
break;
default:
throw new ApiException('event is not support');
}
return('success');
}

private function exchange($from, $to)
{
$from = strtolower($from);
$to = strtolower($to);
$result = file_get_contents("https://cdn.jsdelivr.net/npm/@fawazahmed0/currency-api@latest/v1/currencies/" . $from . ".min.json");
$result = json_decode($result, true);
return $result[$from][$to];
}
}
2 changes: 1 addition & 1 deletion app/Plugins/Telegram/Commands/GetLatestUrl.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

class GetLatestUrl extends Telegram {
public $command = '/getlatesturl';
public $description = '将Telegram账号绑定到网站';
public $description = '获取网站最新网址';

public function handle($message, $match = []) {
$telegramService = $this->telegramService;
Expand Down
17 changes: 17 additions & 0 deletions app/Plugins/Telegram/Commands/Start.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

namespace App\Plugins\Telegram\Commands;

use App\Plugins\Telegram\Telegram;

class Start extends Telegram {
public $command = '/start';
public $description = 'telegram机器人初始化';

public function handle($message, $match = []) {
if (!$message->is_private) return;
$telegramService = $this->telegramService;
$text = "/start 显示所有可用指令\n /bind+空格+订阅链接,将telegram绑定至账户\n /traffic 获取当前使用流量 \n /getlatesturl 获取网站最新网址 \n /unbind 解绑telegram账户";
$telegramService->sendMessage($message->chat_id, $text, 'markdown');
}
}
4 changes: 3 additions & 1 deletion app/Protocols/Surge.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@ public function handle()
'aes-128-gcm',
'aes-192-gcm',
'aes-256-gcm',
'chacha20-ietf-poly1305'
'chacha20-ietf-poly1305',
'2022-blake3-aes-128-gcm',
'2022-blake3-aes-256-gcm',
])
) {
// [Proxy]
Expand Down
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
"paragonie/sodium_compat": "^1.20",
"php-curl-class/php-curl-class": "^8.6",
"spatie/db-dumper": "^3.4",
"stripe/stripe-php": "^7.36.1",
"stripe/stripe-php": "^v14.9.0",
"symfony/http-client": "^6.4",
"symfony/mailgun-mailer": "^6.4",
"symfony/yaml": "*",
Expand Down