Skip to content

Commit

Permalink
Merge pull request #2 from assimon/master
Browse files Browse the repository at this point in the history
同步代码
  • Loading branch information
Julyssn authored Jun 25, 2021
2 parents 4e7b92e + 35039ae commit 925ca6f
Show file tree
Hide file tree
Showing 17 changed files with 308 additions and 149 deletions.
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,11 +67,11 @@ Telegram: [https://t.me/dujiaoka](https://t.me/dujiaoka) (免费,管理相对
- [x] [Paysapi(支付宝/微信)](https://www.paysapi.com/).
- [x] 码支付(QQ/支付宝/微信)
- [x] 微信企业扫码支付
- [x] Paypal支付(默认美元)
- [x] 麻瓜宝数字货币支付
- [x] [Paypal支付(默认美元)](https://www.paypal.com)
- [x] [麻瓜宝数字货币支付](https://mugglepay.com/)
- [x] V免签支付
- [x] 全网易支付支持(针对彩虹版)

- [x] 全网易支付支持(通用彩虹版)
- [x] [stripe](https://stripe.com/)

## 基本环境要求

Expand Down
4 changes: 2 additions & 2 deletions app/Admin/Charts/PayoutRateCard.php
Original file line number Diff line number Diff line change
Expand Up @@ -74,13 +74,13 @@ public function handle(Request $request)
$success = Order::query()
->where('created_at', '>=', $startTime)
->where('created_at', '<=', $endTime)
->where('status', '>', Order::STATUS_PENDING)
->where('status', '>', Order::STATUS_WAIT_PAY)
->count();
// 待支付的数量
$unpaid = Order::query()
->where('created_at', '>=', $startTime)
->where('created_at', '<=', $endTime)
->where('status', Order::STATUS_PENDING)
->where('status', '<=', Order::STATUS_WAIT_PAY)
->count();
$this->withContent($success, $unpaid);
// 图表数据
Expand Down
1 change: 1 addition & 0 deletions app/Admin/Charts/SuccessOrderCard.php
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ public function handle(Request $request)
$orderGroup = Order::query()
->where('created_at', '>=', $startTime)
->where('created_at', '<=', $endTime)
->where('status', Order::STATUS_COMPLETED)
->select(DB::raw('DATE(created_at) as date'), DB::raw('count(id) as num'))
->groupBy('date')
->pluck('num')
Expand Down
2 changes: 1 addition & 1 deletion app/Http/Controllers/Pay/AlipayController.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public function gateway(string $payway, string $orderSN)
$result['actual_price'] = (float)$this->order->actual_price;
$result['orderid'] = $this->order->order_sn;
$result['jump_payuri'] = $result['qr_code'];
return $this->render('static_pages/qrpay', $result);
return $this->render('static_pages/qrpay', $result, __('dujiaoka.scan_qrcode_to_pay'));
} catch (\Exception $e) {
return $this->err(__('dujiaoka.prompt.abnormal_payment_channel') . $e->getMessage());
}
Expand Down
215 changes: 85 additions & 130 deletions app/Http/Controllers/Pay/PaypalPayController.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,11 @@
namespace App\Http\Controllers\Pay;


use App\Exceptions\AppException;
use AmrShawky\LaravelCurrency\Facade\Currency;
use App\Exceptions\RuleValidationException;
use App\Http\Controllers\PayController;
use App\Models\Pays;
use GuzzleHttp\Client;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Redis;
use Illuminate\Support\Facades\Log;
use PayPal\Api\Amount;
use PayPal\Api\Details;
use PayPal\Api\Item;
Expand All @@ -21,170 +20,126 @@
use PayPal\Auth\OAuthTokenCredential;
use PayPal\Exception\PayPalConnectionException;
use PayPal\Rest\ApiContext;
use Illuminate\Support\Facades\Log;

class PaypalPayController extends PayController
{

public function gateway($payway, $oid)
{
$this->checkOrder($payway, $oid);
// paypal id
$clientId = $this->payInfo['merchant_id'];
// paypal 密钥
$clientSecret = $this->payInfo['merchant_pem'];
$acceptUrl = site_url(). $this->payInfo['pay_handleroute'] . '/return_url?order_id='.$this->orderInfo['order_id'];
$currency = 'USD';
$paypal = new ApiContext(
new OAuthTokenCredential(
$clientId,
$clientSecret
)
);
// 正式环境还是沙箱
if (!config('app.paypal_sandebox')) {
$paypal->setConfig(
['mode' => 'live']
);
}
$product = $this->orderInfo['product_name'];
try {
$price = number_format($this->getUsdCurrency($this->orderInfo['actual_price']), 2, '.', '');
} catch (\Exception $exception) {
throw new AppException($exception->getMessage());
}
$shipping = 0;
$total = $price + $shipping;//总价
$description = $this->orderInfo['product_name'];

$payer = (new Payer())->setPaymentMethod('paypal');
$item = (new Item())->setName($product)->setCurrency($currency)->setQuantity(1)->setPrice($price);

$itemList = (new ItemList())->setItems([$item]);

$details = (new Details())->setShipping($shipping)->setSubtotal($price);

$amount = (new Amount())->setCurrency($currency)->setTotal($total)->setDetails($details);
const Currency = 'USD'; //货币单位

$transaction = (new Transaction())->setAmount($amount)->setItemList($itemList)->setDescription($description)->setInvoiceNumber($this->orderInfo['order_id']);
$redirectUrls = (new RedirectUrls())->setReturnUrl($acceptUrl)->setCancelUrl($acceptUrl);

$payment = (new Payment())->setIntent('sale')->setPayer($payer)->setRedirectUrls($redirectUrls)->setTransactions([$transaction]);
public function gateway(string $payway, string $orderSN)
{
try {
// 加载网关
$this->loadGateWay($orderSN, $payway);
$paypal = new ApiContext(
new OAuthTokenCredential(
$this->payGateway->merchant_key,
$this->payGateway->merchant_pem
)
);
$paypal->setConfig(['mode' => 'live']);
$product = $this->order->title;
// 得到汇率
$price = Currency::convert()
->from('CNY')
->to('USD')
->amount($this->order->actual_price)
->get();
$shipping = 0;
$description = $this->order->title;
$total = bcadd($price, $shipping, 2); //总价
$payer = new Payer();
$payer->setPaymentMethod('paypal');
$item = new Item();
$item->setName($product)->setCurrency(self::Currency)->setQuantity($this->order->buy_amount)->setPrice($price);
$itemList = new ItemList();
$itemList->setItems([$item]);
$details = new Details();
$details->setShipping($shipping)->setSubtotal($price);
$amount = new Amount();
$amount->setCurrency(self::Currency)->setTotal($total)->setDetails($details);
$transaction = new Transaction();
$transaction->setAmount($amount)->setItemList($itemList)->setDescription($description)->setInvoiceNumber($this->order->order_sn);
$redirectUrls = new RedirectUrls();
$redirectUrls->setReturnUrl(route('paypal-return', ['success' => 'ok', 'orderSN' => $this->order->order_sn]))->setCancelUrl(route('paypal-return', ['success' => 'no', 'orderSN' => $this->order->order_sn]));
$payment = new Payment();
$payment->setIntent('sale')->setPayer($payer)->setRedirectUrls($redirectUrls)->setTransactions([$transaction]);
$payment->create($paypal);
} catch (PayPalConnectionException $e) {
throw new AppException(__('prompt.abnormal_payment_channel') . $e->getData());
$approvalUrl = $payment->getApprovalLink();
return redirect($approvalUrl);
} catch (PayPalConnectionException $payPalConnectionException) {
return $this->err($payPalConnectionException->getMessage());
} catch (RuleValidationException $exception) {
return $this->err($exception->getMessage());
}
$approvalUrl = $payment->getApprovalLink();
return redirect($approvalUrl);
}

/**
*paypal 同步回调
*/
public function returnUrl(Request $request)
{
$oid = $request->get('order_id');
$paymentId = $request->get('paymentId');
$payerId = $request->get('PayerID');
$cacheord = json_decode(Redis::hget('PENDING_ORDERS_LIST', $oid), true);
if (!$cacheord) {
$success = $request->input('success');
$paymentId = $request->input('paymentId');
$payerID = $request->input('PayerID');
$orderSN = $request->input('orderSN');
if ($success == 'no' || empty($paymentId) || empty($payerID)) {
// 取消支付
redirect(url('detail-order-sn', ['orderSN' => $payerID]));
}
$order = $this->orderService->detailOrderSN($orderSN);
if (!$order) {
return 'error';
}
$payGateway = $this->payService->detail($order->pay_id);
if (!$payGateway) {
return 'error';
}
$payInfo = Pays::where('id', $cacheord['pay_way'])->first()->toArray();
$paypal = new ApiContext(
new OAuthTokenCredential(
$payInfo['merchant_id'],
$payInfo['merchant_pem']
$payGateway->merchant_key,
$payGateway->merchant_pem
)
);
// 正式环境还是沙箱
if (!config('app.paypal_sandebox')) {
$paypal->setConfig(
['mode' => 'live']
);
}
$paypal->setConfig(['mode' => 'live']);
$payment = Payment::get($paymentId, $paypal);
$execute = new PaymentExecution();
$execute->setPayerId($payerId);
try{
$result = $payment->execute($execute, $paypal);
$payData = $result->toArray();
if ($payData['payer']['status'] == "VERIFIED" && $payData['transactions'][0]['amount']['currency'] == "USD") {
$this->orderService->successOrder($oid, $paymentId, $cacheord['actual_price']);
return redirect(site_url().'searchOrderById?order_id='.$oid);
}
} catch(\Exception $e) {
throw new AppException(__('prompt.abnormal_payment_channel') . $e->getMessage());
$execute->setPayerId($payerID);
try {
$payment->execute($execute, $paypal);
$this->orderProcessService->completedOrder($orderSN, $order->actual_price, $paymentId);
Log::info("paypal支付成功", ['支付成功,支付ID【' . $paymentId . '】,支付人ID【' . $payerID . '']);
} catch (\Exception $e) {
Log::error("paypal支付失败", ['支付失败,支付ID【' . $paymentId . '】,支付人ID【' . $payerID . '']);
}
return redirect(url('detail-order-sn', ['orderSN' => $orderSN]));
}


/**
* 异步通知
* TODO: 暂未实现,但是好像只实现同步回调即可。这个可以放在后面实现
*/
public function notifyUrl(Request $request)
{
$data = $request->post();
if (!isset($data['resource']['transactions'])) return;
$oid = $data['resource']['transactions'][0]['invoice_number'];
$paymentId = $data['resource']['id'];
$payerId =$data['resource']['payer']['payer_info']['payer_id'];
$cacheord = json_decode(Redis::hget('PENDING_ORDERS_LIST', $oid), true);
if (!$cacheord) {
return 'error';
}
$payInfo = Pays::where('id', $cacheord['pay_way'])->first()->toArray();
$paypal = new ApiContext(
new OAuthTokenCredential(
$payInfo['merchant_id'],
$payInfo['merchant_pem']
)
);
// 正式环境还是沙箱
if (!config('app.paypal_sandebox')) {
$paypal->setConfig(
['mode' => 'live']
);
}
$payment = Payment::get($paymentId, $paypal);
$execute = new PaymentExecution();
$execute->setPayerId($payerId);
try{
$result = $payment->execute($execute, $paypal);
$payData = $result->toArray();
if ($payData['payer']['status'] == "VERIFIED" && $payData['transactions'][0]['amount']['currency'] == "USD") {
$this->orderService->successOrder($oid, $paymentId, $cacheord['actual_price']);
}
} catch(\Exception $e) {
Log::info('paypal异常:' . $e->getMessage());
//获取回调结果
$json_data = $this->get_JsonData();
if(!empty($json_data)){
Log::debug("paypal notify info:\r\n" . json_encode($json_data));
}else{
Log::debug("paypal notify fail:参加为空");
}

}

/**
* 根据RMB获取美元
* @param $cny
* @return float|int
* @throws \Exception
*/
public function getUsdCurrency($cny)
private function get_JsonData()
{
$client = new Client();
$res = $client->get('https://m.cmbchina.com/api/rate/getfxrate');
$fxrate = json_decode($res->getBody(), true);
if (!isset($fxrate['data'])) {
throw new \Exception('汇率接口异常');
$json = file_get_contents('php://input');
if ($json) {
$json = str_replace("'", '', $json);
$json = json_decode($json,true);
}
$dfFxrate = 0.13;
foreach ($fxrate['data'] as $item) {
if ($item['ZCcyNbr'] == "美元") {
$dfFxrate = 100 / $item['ZRtcOfr'];
break;
}
}
return $cny * $dfFxrate;
return $json;
}


}
2 changes: 1 addition & 1 deletion app/Http/Controllers/Pay/StripeController.php
Original file line number Diff line number Diff line change
Expand Up @@ -496,7 +496,7 @@ public function charge(Request $request)
$payGateway = $this->payService->detail($cacheord->pay_id);
\Stripe\Stripe::setApiKey($payGateway -> merchant_pem);
$result = \Stripe\Charge::create([
'amount' => bcmul($this->getUsdCurrency($cacheord->actual_price), 100,2),
'amount' => bcmul($this->getUsdCurrency($cacheord->actual_price), 100,0),
'currency' => 'usd',
'source' => $data['stripeToken'],
]);
Expand Down
2 changes: 1 addition & 1 deletion app/Http/Controllers/Pay/VpayController.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public function gateway(string $payway, string $orderSN)
"payId" => date('YmdHis') . rand(1, 65535),//平台ID号
"price" => (float)$this->order->actual_price,//原价
'param' => $this->order->order_sn,
'returnUrl' => url($this->payGateway->pay_handleroute . '/return_url', ['order_id' => $this->order->order_sn]),
'returnUrl' => route('vpay-return', ['order_id' => $this->order->order_sn]),
'notifyUrl' => url($this->payGateway->pay_handleroute . '/notify_url'),
"isHtml" => 1,
);
Expand Down
2 changes: 1 addition & 1 deletion app/Http/Controllers/Pay/WepayController.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public function gateway(string $payway, string $orderSN)
$result['payname'] =$this->payGateway->pay_name;
$result['actual_price'] = (float)$this->order->actual_price;
$result['orderid'] = $this->order->order_sn;
return $this->view('static_pages/qrpay', $result);
return $this->render('static_pages/qrpay', $result, __('dujiaoka.scan_qrcode_to_pay'));
} catch (\Exception $e) {
throw new RuleValidationException(__('dujiaoka.prompt.abnormal_payment_channel') . $e->getMessage());
}
Expand Down
2 changes: 1 addition & 1 deletion app/Http/Middleware/VerifyCsrfToken.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,6 @@ class VerifyCsrfToken extends Middleware
* @var array
*/
protected $except = [
//
'pay/*',
];
}
2 changes: 2 additions & 0 deletions app/Service/OrderProcessService.php
Original file line number Diff line number Diff line change
Expand Up @@ -447,6 +447,8 @@ public function processManual(Order $order)
'ord_title' => $order->title,
'order_id' => $order->order_sn,
'buy_amount' => $order->buy_amount,
'ord_price' => $order->actual_price,
'created_at' => $order->created_at,
];
$tpl = $this->emailtplService->detailByToken('manual_send_manage_mail');
$mailBody = replace_mail_tpl($tpl, $mailData);
Expand Down
2 changes: 1 addition & 1 deletion app/Service/OrderService.php
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ public function validatorChargeInput(Goods $goods, Request $request): string
* @copyright assimon<[email protected]>
* @link http://utf8.hk/
*/
public function detailOrderSN(string $orderSN): Order
public function detailOrderSN(string $orderSN):? Order
{
$order = Order::query()->with(['coupon', 'pay', 'goods'])->where('order_sn', $orderSN)->first();
return $order;
Expand Down
2 changes: 2 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"license": "MIT",
"require": {
"php": "^7.2.5|^8.0",
"amrshawky/laravel-currency": "^4.0",
"dcat/easy-excel": "^1.0",
"dcat/laravel-admin": "2.*",
"fideloper/proxy": "^4.4",
Expand All @@ -17,6 +18,7 @@
"laravel/framework": "^6.20.26",
"laravel/tinker": "^2.5",
"mews/captcha": "^3.2",
"paypal/rest-api-sdk-php": "^1.14",
"simplesoftwareio/simple-qrcode": "2.0.0",
"stripe/stripe-php": "^7.84",
"xhat/payjs-laravel": "^1.6",
Expand Down
Loading

0 comments on commit 925ca6f

Please sign in to comment.