forked from LightCountry/TokenPay
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
2817776
commit 7bf6bc3
Showing
16 changed files
with
589 additions
and
61 deletions.
There are no files selected for viewing
Binary file not shown.
101 changes: 101 additions & 0 deletions
101
Plugs/dujiaoka/app/Http/Controllers/Pay/TokenPayController.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
<?php | ||
/** | ||
* The file was created by Assimon. | ||
* | ||
* @author assimon<[email protected]> | ||
* @copyright assimon<[email protected]> | ||
* @link http://utf8.hk/ | ||
*/ | ||
|
||
namespace App\Http\Controllers\Pay; | ||
|
||
|
||
use App\Exceptions\RuleValidationException; | ||
use App\Http\Controllers\PayController; | ||
use GuzzleHttp\Client; | ||
use GuzzleHttp\Exception\GuzzleException; | ||
use Illuminate\Http\Request; | ||
|
||
class TokenPayController extends PayController | ||
{ | ||
public function gateway(string $payway, string $orderSN) | ||
{ | ||
try { | ||
// 加载网关 | ||
$this->loadGateWay($orderSN, $payway); | ||
//构造要请求的参数数组,无需改动 | ||
$parameter = [ | ||
"ActualAmount" => (float)$this->order->actual_price,//原价 | ||
"OutOrderId" => $this->order->order_sn, | ||
// 当前为每个用户分配一个收款地址,通过此参数识别是否为同一个用户 | ||
// 如需要每单一个地址,可将此行改为 "OrderUserKey" => $this->order->order_sn, | ||
"OrderUserKey" => $this->order->email, | ||
"Currency" => $this->payGateway->merchant_id, | ||
'RedirectUrl' => route('tokenpay-return', ['order_id' => $this->order->order_sn]), | ||
'NotifyUrl' => url($this->payGateway->pay_handleroute . '/notify_url'), | ||
]; | ||
$client = new Client(); | ||
$response = $client->post($this->payGateway->merchant_pem, ['form_params' => $parameter]); | ||
$body = json_decode($response->getBody()->getContents(), true); | ||
if (!isset($body['success']) || $body['success'] != true) { | ||
return $this->err(__('dujiaoka.prompt.abnormal_payment_channel') . $body['message']); | ||
} | ||
return redirect()->away($body['data']); | ||
} catch (RuleValidationException $exception) { | ||
} catch (GuzzleException $exception) { | ||
return $this->err($exception->getMessage()); | ||
} | ||
} | ||
|
||
private function VerifySign(array $parameter, string $signKey) | ||
{ | ||
ksort($parameter); | ||
reset($parameter); //内部指针指向数组中的第一个元素 | ||
$sign = ''; | ||
$urls = ''; | ||
foreach ($parameter as $key => $val) { | ||
if ($key != 'Signature') { | ||
if ($sign != '') { | ||
$sign .= "&"; | ||
$urls .= "&"; | ||
} | ||
$sign .= "$key=$val"; //拼接为url参数形式 | ||
$urls .= "$key=" . urlencode($val); //拼接为url参数形式 | ||
} | ||
} | ||
$sign = md5($sign . $signKey);//密码追加进入开始MD5签名 | ||
return $sign; | ||
} | ||
|
||
public function notifyUrl(Request $request) | ||
{ | ||
$data = $request->all(); | ||
$order = $this->orderService->detailOrderSN($data['OutOrderId']); | ||
if (!$order) { | ||
return 'fail'; | ||
} | ||
$payGateway = $this->payService->detail($order->pay_id); | ||
if (!$payGateway) { | ||
return 'fail'; | ||
} | ||
//合法的数据 | ||
$signature = $this->VerifySign($data, $payGateway->merchant_key); | ||
if ($data['Signature'] != $signature) { //不合法的数据 | ||
return 'fail'; //返回失败 继续补单 | ||
} else { | ||
//合法的数据 | ||
//业务处理 | ||
$this->orderProcessService->completedOrder($data['OutOrderId'], $data['ActualAmount'], $data['Id']); | ||
return 'ok'; | ||
} | ||
} | ||
|
||
public function returnUrl(Request $request) | ||
{ | ||
$oid = $request->get('order_id'); | ||
// 异步通知还没到就跳转了,所以这里休眠2秒 | ||
sleep(2); | ||
return redirect(url('detail-order-sn', ['orderSN' => $oid])); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
INSERT INTO `pays` (`pay_name`, `pay_check`, `pay_method`, `pay_client`, `merchant_id`, `merchant_key`, `merchant_pem`, `pay_handleroute`, `is_open`, `created_at`, `updated_at`, `deleted_at`) VALUES ('USDT-TRC20', 'tokenpay-usdt-trc', 1, 3, 'USDT_TRC20', '你的API密钥', 'https://token-pay.xxx.com/CreateOrder', 'pay/tokenpay', 1, now(), now(), NULL); | ||
INSERT INTO `pays` (`pay_name`, `pay_check`, `pay_method`, `pay_client`, `merchant_id`, `merchant_key`, `merchant_pem`, `pay_handleroute`, `is_open`, `created_at`, `updated_at`, `deleted_at`) VALUES ('TRX', 'tokenpay-trx', 1, 3, 'TRX', '你的API密钥', 'https://token-pay.xxx.com/CreateOrder', 'pay/tokenpay', 1, now(), now(), NULL); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
<?php | ||
/** | ||
* The file was created by Assimon. | ||
* | ||
* @author assimon<[email protected]> | ||
* @copyright assimon<[email protected]> | ||
* @link http://utf8.hk/ | ||
*/ | ||
use Illuminate\Support\Facades\Route; | ||
|
||
Route::get('pay-gateway/{handle}/{payway}/{orderSN}', 'PayController@redirectGateway'); | ||
|
||
// 支付相关 | ||
Route::group(['prefix' => 'pay', 'namespace' => 'Pay', 'middleware' => ['dujiaoka.pay_gate_way']], function () { | ||
// 支付宝 | ||
Route::get('alipay/{payway}/{orderSN}', 'AlipayController@gateway'); | ||
Route::post('alipay/notify_url', 'AlipayController@notifyUrl'); | ||
// 微信 | ||
Route::get('wepay/{payway}/{orderSN}', 'WepayController@gateway'); | ||
Route::post('wepay/notify_url', 'WepayController@notifyUrl'); | ||
// 码支付 | ||
Route::get('mapay/{payway}/{orderSN}', 'MapayController@gateway'); | ||
Route::post('mapay/notify_url', 'MapayController@notifyUrl'); | ||
// Paysapi | ||
Route::get('paysapi/{payway}/{orderSN}', 'PaysapiController@gateway'); | ||
Route::post('paysapi/notify_url', 'PaysapiController@notifyUrl'); | ||
Route::get('paysapi/return_url', 'PaysapiController@returnUrl')->name('paysapi-return'); | ||
// payjs | ||
Route::get('payjs/{payway}/{orderSN}', 'PayjsController@gateway'); | ||
Route::post('payjs/notify_url', 'PayjsController@notifyUrl'); | ||
// 易支付 | ||
Route::get('yipay/{payway}/{orderSN}', 'YipayController@gateway'); | ||
Route::get('yipay/notify_url', 'YipayController@notifyUrl'); | ||
Route::get('yipay/return_url', 'YipayController@returnUrl')->name('yipay-return'); | ||
// paypal | ||
Route::get('paypal/{payway}/{orderSN}', 'PaypalPayController@gateway'); | ||
Route::get('paypal/return_url', 'PaypalPayController@returnUrl')->name('paypal-return'); | ||
Route::any('paypal/notify_url', 'PaypalPayController@notifyUrl'); | ||
// V免签 | ||
Route::get('vpay/{payway}/{orderSN}', 'VpayController@gateway'); | ||
Route::get('vpay/notify_url', 'VpayController@notifyUrl'); | ||
Route::get('vpay/return_url', 'VpayController@returnUrl')->name('vpay-return'); | ||
// stripe | ||
Route::get('stripe/{payway}/{oid}','StripeController@gateway'); | ||
Route::get('stripe/return_url','StripeController@returnUrl'); | ||
Route::get('stripe/check','StripeController@check'); | ||
Route::get('stripe/charge','StripeController@charge'); | ||
// Coinbase | ||
Route::get('coinbase/{payway}/{orderSN}', 'CoinbaseController@gateway'); | ||
Route::post('coinbase/notify_url', 'CoinbaseController@notifyUrl'); | ||
// epusdt | ||
Route::get('epusdt/{payway}/{orderSN}', 'EpusdtController@gateway'); | ||
Route::post('epusdt/notify_url', 'EpusdtController@notifyUrl'); | ||
Route::get('epusdt/return_url', 'EpusdtController@returnUrl')->name('epusdt-return'); | ||
// tokenpay | ||
Route::get('tokenpay/{payway}/{orderSN}', 'TokenPayController@gateway'); | ||
Route::post('tokenpay/notify_url', 'TokenPayController@notifyUrl'); | ||
Route::get('tokenpay/return_url', 'TokenPayController@returnUrl')->name('tokenpay-return'); | ||
|
||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
using Flurl; | ||
using Flurl.Http; | ||
using FreeSql; | ||
using TokenPay.Domains; | ||
using TokenPay.Extensions; | ||
using TokenPay.Models.TronModel; | ||
|
||
namespace TokenPay.BgServices | ||
{ | ||
public class OrderCheckTRXService : BaseScheduledService | ||
{ | ||
private readonly ILogger<OrderCheckTRXService> _logger; | ||
private readonly IConfiguration _configuration; | ||
private readonly IHostEnvironment env; | ||
private readonly IServiceProvider _serviceProvider; | ||
|
||
public OrderCheckTRXService(ILogger<OrderCheckTRXService> logger, | ||
IConfiguration configuration, | ||
IHostEnvironment env, | ||
IServiceProvider serviceProvider) : base("TRX订单检测", TimeSpan.FromSeconds(3), logger) | ||
{ | ||
_logger = logger; | ||
this._configuration = configuration; | ||
this.env = env; | ||
_serviceProvider = serviceProvider; | ||
} | ||
|
||
protected override async Task ExecuteAsync() | ||
{ | ||
using IServiceScope scope = _serviceProvider.CreateScope(); | ||
var _repository = scope.ServiceProvider.GetRequiredService<IBaseRepository<TokenOrders>>(); | ||
|
||
var Address = await _repository | ||
.Where(x => x.Status == OrderStatus.Pending) | ||
.Where(x => x.Currency == Currency.TRX) | ||
.ToListAsync(x => x.ToAddress); | ||
if (Address.Count > 0) | ||
_logger.LogInformation("待检测地址数:{c}", Address.Count); | ||
var BaseUrl = "https://api.trongrid.io"; | ||
if (!env.IsProduction()) | ||
{ | ||
BaseUrl = "https://api.shasta.trongrid.io"; | ||
} | ||
var OnlyConfirmed = _configuration.GetValue("OnlyConfirmed", true); | ||
var start = DateTime.Now.AddMinutes(-120); | ||
foreach (var address in Address) | ||
{ | ||
//查询此地址待支付订单 | ||
var orders = await _repository | ||
.Where(x => x.Status == OrderStatus.Pending) | ||
.Where(x => x.Currency == Currency.TRX) | ||
.Where(x => x.ToAddress == address) | ||
.OrderBy(x => x.CreateTime) | ||
.ToListAsync(); | ||
if (!orders.Any()) | ||
{ | ||
continue; | ||
} | ||
var query = new Dictionary<string, object>(); | ||
if (OnlyConfirmed) | ||
{ | ||
query.Add("only_confirmed", true); | ||
} | ||
else | ||
{ | ||
query.Add("only_unconfirmed", true); | ||
} | ||
query.Add("only_to", true); | ||
query.Add("limit", 50); | ||
query.Add("min_timestamp", start.ToUnixTimeStamp()); | ||
var req = BaseUrl | ||
.AppendPathSegment($"v1/accounts/{address}/transactions") | ||
.WithTimeout(15); | ||
if (!env.IsProduction()) | ||
req = req.WithHeader("TRON-PRO-API-KEY", _configuration.GetValue("TRON-PRO-API-KEY", "")); | ||
var result = await req | ||
.GetJsonAsync<BaseResponse<TrxTransaction>>(); | ||
|
||
if (result.Success && result.Data?.Count > 0) | ||
{ | ||
foreach (var item in result.Data) | ||
{ | ||
//没有需要匹配的订单了 | ||
if (!orders.Any()) | ||
{ | ||
break; | ||
} | ||
//此交易已被其他订单使用 | ||
if (await _repository.Select.AnyAsync(x => x.BlockTransactionId == item.TxID)) | ||
{ | ||
continue; | ||
} | ||
var raw = item.RawData.Contract.FirstOrDefault(x => x.Type == "TransferContract")?.Parameter?.Value; | ||
if (raw == null || raw.AssetName != null) | ||
{ | ||
continue; | ||
} | ||
var order = orders.Where(x => x.Amount == raw.RealAmount && x.ToAddress == raw.ToAddressBase58 && x.CreateTime < item.BlockTimestamp.ToDateTime()) | ||
.OrderByDescending(x=>x.CreateTime)//优先付最后一单 | ||
.FirstOrDefault(); | ||
if (order != null) | ||
{ | ||
order.FromAddress = raw.OwnerAddress; | ||
order.BlockTransactionId = item.TxID; | ||
order.Status = OrderStatus.Paid; | ||
order.PayTime = DateTime.Now; | ||
await _repository.UpdateAsync(order); | ||
orders.Remove(order); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
|
||
private async Task<bool> Notify(TokenOrders order) | ||
{ | ||
if (!string.IsNullOrEmpty(order.NotifyUrl)) | ||
{ | ||
try | ||
{ | ||
var result = await order.NotifyUrl.PostJsonAsync(order); | ||
var message = await result.GetStringAsync(); | ||
if (result.StatusCode == 200) | ||
{ | ||
_logger.LogInformation("订单异步通知成功!\n{msg}", message); | ||
return true; | ||
} | ||
else | ||
{ | ||
_logger.LogInformation("订单异步通知失败:{msg}", message); | ||
} | ||
} | ||
catch (Exception e) | ||
{ | ||
_logger.LogInformation("订单异步通知失败:{msg}", e.Message); | ||
} | ||
} | ||
return false; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.