paymentservice/app/Http/Controllers/StripeController.php

534 lines
22 KiB
PHP
Raw Normal View History

2023-05-30 01:37:16 +00:00
<?php
namespace App\Http\Controllers;
use App\Enums\PaymentTransactionStatus;
use App\Models\PaymentAggregator;
use App\Models\PaymentRefund;
2023-05-30 01:37:16 +00:00
use App\Models\PaymentTransaction;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Str;
use Stripe\PaymentIntent;
2023-05-30 01:37:16 +00:00
use Stripe\StripeClient;
use Throwable;
class StripeController extends Controller
{
private $client;
private $timeout = 60; //In seconds
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
// Create a client with a base URI
$this->client = new StripeClient(config('variables.stripe_secret'));
}
public function getMethods()
{
$data = $this->client->paymentMethods->all()->toArray();
return $this->successResponse([
'hasWebview' => true,
'methods' => $data,
]);
}
public function getCheckout($payment_token)
{
$transaction = PaymentTransaction::where('payment_token', $payment_token)->first();
if(empty($transaction)){
return $this->errorResponse(__('errors.model_not_found', ['model' => 'transaction']), Response::HTTP_NOT_FOUND);
}
$amount = $transaction->amount;
$receiver = config('variables.receiver_name');
$receiver_logo = asset('assets/images/logo.jpeg');
return view('stripe-checkout', compact('amount','receiver','receiver_logo','payment_token'));
}
public function pay(Request $request)
{
try {
$this->validate($request, [
'aggregator_id' => 'required|integer',
'amount' => 'required|numeric|min:5',
'currency' => 'required|string|size:3',
'customer_id' => 'nullable',
2023-05-30 01:37:16 +00:00
'payment_method' => 'nullable|string',
'customer_email' => 'required|email',
'customer_name' => 'nullable|string',
2023-05-30 01:37:16 +00:00
'customer_surname' => 'required|string',
'customer_phone_number' => 'nullable|string',
2023-05-30 01:37:16 +00:00
'customer_address' => 'required|string',
'customer_city' => 'required_if:payment_method,CARD|string',
2023-05-30 01:37:16 +00:00
'customer_country' => 'required|string|size:2',
'customer_state' => 'required_if:payment_method,CARD|string', //Etat du pays dans lequel se trouve le client. Cette valeur est obligatoire si le client se trouve au États Unis dAmérique (US) ou au Canada (CA)
'customer_zip_code' => 'required_if:payment_method,CARD|string|size:5',
2023-05-30 01:37:16 +00:00
'reason' => 'required|string'
]);
$transaction_id = $this->getTransactionID();
$payment_method = $request->input('payment_method','CARD');
2023-05-30 01:37:16 +00:00
$amount = $request->input('amount');
$currency = $request->input('currency');
if($amount < 325 && $currency == 'XAF'){
$amount = 325;
}
$payment_token = $this->getTransactionToken();
$payment_url = route('stripe.checkout',['payment_token' =>$payment_token]);
PaymentTransaction::create([
'aggregator_id' => $request->input('aggregator_id'),
"currency" => $request->input('currency'),
"transaction_id" => $transaction_id,
"amount" => $amount,
"payment_method" => $payment_method,
"payment_token" => $payment_token,
"payment_url" => $payment_url,
'status' => PaymentTransactionStatus::INITIATED,
2023-05-30 01:37:16 +00:00
"reason" => $request->input('reason'),
"customer_id" => $request->input('customer_id'),
"customer_name" => $request->input('customer_name'),
"customer_surname" => $request->input('customer_surname'),
"customer_email" => $request->input('customer_email'),
"customer_phone_number" => $request->input('customer_phone_number'),
"customer_address" => $request->input('customer_address'),
"customer_city" => $request->input('customer_city'),
"customer_country" => $request->input('customer_country'),
"customer_state" => $request->input('customer_state'),
"customer_zip_code" => $request->input('customer_zip_code'),
]);
return $this->successResponse([
'message' => 'Payment initiated',
'payment_url' => $payment_url
]);
}catch (Throwable $e){
Log::error($e->getMessage());
}
return $this->errorResponse(trans('errors.unexpected_error'));
}
public function post(Request $request)
{
$this->validate($request, [
'payment_token' => 'required|string|exists:payment_transactions,payment_token',
'stripeToken' => 'required|string',
]);
$token = $request->input('payment_token');
$transaction = PaymentTransaction::where('payment_token', $token)->where('status', PaymentTransactionStatus::INITIATED)->firstOrFail();
2023-05-30 01:37:16 +00:00
// Init payment
$charge = $this->client->charges->create([
"amount" => stripeFormatCurrency(round($transaction->amount,2), $transaction->currency),
2023-05-30 01:37:16 +00:00
"currency" => $transaction->currency,
"description" => $transaction->reason,
// "customer" => 15,
"source" => $request->input('stripeToken'),
"receipt_email" => $transaction->customer_email,
"metadata" => [
"transaction_id" => $transaction->transaction_id,
'lang' => app()->getLocale(),
"customer_id" => $transaction->customer_id,
"customer_name" => $transaction->customer_name,
"customer_surname" => $transaction->customer_surname,
"customer_email" => $transaction->customer_email,
"customer_phone_number" => $transaction->customer_phone_number,
"customer_address" => $transaction->customer_address,
"customer_city" => $transaction->customer_city,
"customer_country" => $transaction->customer_country,
"customer_state" => $transaction->customer_state,
"customer_zip_code" => $transaction->customer_zip_code,
],
]);
if($charge){
$transaction->update([
'aggregator_payment_ref' => $charge->id,
'payment_date' => date('Y-m-d H:i:s', $charge->created),
'status' => PaymentTransactionStatus::ACCEPTED,
2023-05-30 01:37:16 +00:00
'payment_method_exact' => $request->input('card_number')
]);
return redirect()->route('checkout',['payment_token' => $token]);
}
session()->flash('error',__('errors.unexpected_error'));
return redirect()->to(route('stripe.checkout',['payment_token' => $token]));
}
private function getTransactionToken()
{
do {
$code = 'STP-'.Str::random(64);
$result = collect(DB::select('SELECT * FROM payment_transactions WHERE payment_token = :code', ['code' => $code]));
$codeCorrect = sizeof($result) < 0;
} while ($codeCorrect);
return $code;
}
public function refund(Request $request)
{
$this->validate($request, [
'transaction_id' => 'required|string|exists:payment_transactions,transaction_id',
'reason' => 'nullable|string|in:duplicate,fraudulent,requested_by_customer'
]);
$transaction = PaymentTransaction::where('transaction_id', $request->input('transaction_id'))->where('status', PaymentTransactionStatus::ACCEPTED)->firstOrFail();
$reason = $request->input('reason','requested_by_customer');
try{
$reference = $transaction->aggregator_payment_ref;
$twoFirstChars = substr($reference,0,2);
$keyToRefund = $twoFirstChars == 'ch' ? 'charge' : 'payment_intent';
$refund = $this->client->refunds->create([
$keyToRefund => $reference,
'reason' => $reason
]);
if(!empty($refund)){
PaymentRefund::create([
'refund_id' => $this->getTransactionID('payment_refunds'),
'transaction_id' => $transaction->id,
"currency" => strtoupper($refund->currency),
"amount" => $refund->amount,
"reason" => $reason,
'date' => date('Y-m-d H:i:s', $refund->created),
'status' => strtoupper($refund->status)
]);
return $this->successResponse("Payment refunded successfully");
}
}catch (Throwable $e){
Log::error("Error Stripe refund");
$errorMessage = $e->getMessage();
Log::error($errorMessage);
}
return $this->errorResponse($errorMessage ?? __('errors.unexpected_error'));
}
public function payIn(Request $request)
{
$this->validate($request, [
'card_no' => 'required',
'exp_month' => 'required',
'exp_year' => 'required',
'cvc' => 'required',
// 'aggregator_id' => 'required|integer',
'amount' => 'required|numeric|min:5',
'currency' => 'required|string|size:3',
'customer_id' => 'nullable',
// 'payment_method' => 'nullable|string', // Actuallu it's only CARD
'customer_email' => 'required|email',
'customer_name' => 'nullable|string',
'customer_surname' => 'required|string',
'customer_phone_number' => 'nullable|string',
'customer_address' => 'required|string',
'customer_city' => 'required|string',
'customer_country' => 'required|string|size:2',
'customer_state' => 'nullable|string', //Etat du pays dans lequel se trouve le client. Cette valeur est obligatoire si le client se trouve au États Unis dAmérique (US) ou au Canada (CA)
'customer_zip_code' => 'nullable|string|size:5',
'reason' => 'required|string'
]);
$aggregator = PaymentAggregator::where('name','like','%stripe%')->firstOrFail();
try{
$amount = $request->input('amount');
$currency = $request->input('currency');
if($amount < 325 && $currency == 'XAF'){
$amount = 325;
}
$transaction = PaymentTransaction::create([
'aggregator_id' => $aggregator->id,
"currency" => $request->input('currency'),
"transaction_id" => $this->getTransactionID(),
"amount" => $amount,
"payment_method" => 'CARD',
'status' => PaymentTransactionStatus::INITIATED,
"reason" => $request->input('reason'),
"customer_id" => $request->input('customer_id'),
"customer_name" => $request->input('customer_name'),
"customer_surname" => $request->input('customer_surname'),
"customer_email" => $request->input('customer_email'),
"customer_phone_number" => $request->input('customer_phone_number'),
"customer_address" => $request->input('customer_address'),
"customer_city" => $request->input('customer_city'),
"customer_country" => $request->input('customer_country'),
"customer_state" => $request->input('customer_state'),
"customer_zip_code" => $request->input('customer_zip_code'),
]);
// Create payment method in Stripe
// https://stripe.com/docs/api/payment_methods/create
$method = $this->client->paymentMethods->create([
'type' => 'card',
'card' => [
'number' => $request->input('card_no'),
'exp_month' => $request->input('exp_month'),
'exp_year' => $request->input('exp_year'),
'cvc' => $request->input('cvc'),
]
]);
if($method){
$transaction->update([
'payment_method_exact' => $method->id
]);
// Create payment intent
// https://stripe.com/docs/api/payment_intents/create
$paymentIntent = $this->client->paymentIntents->create([
"amount" => stripeFormatCurrency($amount, $request->input('currency')),
"currency" => $request->input('currency'),
"description" => $request->input('reason'),
'payment_method_types' => ['card'],
'payment_method' => $method->id,
'confirm' => 'true',
'capture_method' => 'automatic',
'return_url' => route('stripe.webhook',['transaction_id' => $transaction->transaction_id]), // Redirect url if 3d secure required
'payment_method_options' => [
'card' => [
'request_three_d_secure' => 'automatic'
]
],
]);
if($paymentIntent){
$transaction->update([
'payment_date' => date('Y-m-d H:i:s', $paymentIntent->created),
'aggregator_payment_ref' => $paymentIntent->id
]);
if(!empty($paymentIntent->next_action->redirect_to_url->url)){
return $this->successResponse([
'message' => '3D secure redirect',
'payment_url' => $paymentIntent->next_action->redirect_to_url->url
], Response::HTTP_MOVED_PERMANENTLY);
}
return $this->handlePaymentIntentResult($transaction, $paymentIntent);
}
}
}catch (Throwable $e){
Log::error("Error Stripe submit payment intent");
$errorMessage = $e->getMessage();
Log::error($errorMessage);
}
return $this->errorResponse($errorMessage ?? __('errors.unexpected_error'));
}
public function payOut(Request $request)
{
$this->validate($request, [
'card_no' => 'required_if:payment_method,CARD',
'exp_month' => 'required_if:payment_method,CARD',
'exp_year' => 'required_if:payment_method,CARD',
2023-06-14 14:49:24 +00:00
// 'cvc' => 'required_if:payment_method,CARD',
'bank_country' => 'required_if:payment_method,BANK|string|size:2',
'bank_currency' => 'required_if:payment_method,BANK|string|size:3',
'bank_account_number' => 'required_if:payment_method,BANK|string',
// 'aggregator_id' => 'required|integer',
'amount' => 'required|numeric|min:5',
'currency' => 'required|string|size:3',
'customer_id' => 'nullable',
'payment_method' => 'required|string|in:CARD,BANK',
'customer_email' => 'required|email',
'customer_name' => 'nullable|string',
'customer_surname' => 'required|string',
'customer_phone_number' => 'nullable|string',
'customer_address' => 'required|string',
'customer_city' => 'required|string',
'customer_country' => 'required|string|size:2',
'customer_state' => 'nullable|string', //Etat du pays dans lequel se trouve le client. Cette valeur est obligatoire si le client se trouve au États Unis dAmérique (US) ou au Canada (CA)
'customer_zip_code' => 'nullable|string|size:5',
'reason' => 'required|string'
]);
$aggregator = PaymentAggregator::where('name','like','%stripe%')->firstOrFail();
try{
$amount = $request->input('amount');
$currency = $request->input('currency');
$payment_method = $request->input('payment_method');
$transaction = PaymentTransaction::create([
'aggregator_id' => $aggregator->id,
"currency" => $request->input('currency'),
"transaction_id" => $this->getTransactionID(),
"amount" => $amount,
"payment_method" => $payment_method,
'status' => PaymentTransactionStatus::INITIATED,
"reason" => $request->input('reason'),
"customer_id" => $request->input('customer_id'),
"customer_name" => $request->input('customer_name'),
"customer_surname" => $request->input('customer_surname'),
"customer_email" => $request->input('customer_email'),
"customer_phone_number" => $request->input('customer_phone_number'),
"customer_address" => $request->input('customer_address'),
"customer_city" => $request->input('customer_city'),
"customer_country" => $request->input('customer_country'),
"customer_state" => $request->input('customer_state'),
"customer_zip_code" => $request->input('customer_zip_code'),
]);
// Create destination account in Stripe
if($payment_method == 'CARD'){
//https://stripe.com/docs/api/external_account_cards/create
$destination = $this->client->accounts->createExternalAccount(
config('variables.stripe_account'), [
'external_account' => [
'object' => 'card',
'number' => $request->input('card_no'),
'exp_month' => $request->input('exp_month'),
'exp_year' => $request->input('exp_year'),
2023-06-14 14:49:24 +00:00
// 'cvc' => $request->input('cvc'),
]
]
);
}else{
//https://stripe.com/docs/api/external_account_bank_accounts/create#account_create_bank_account
$destination = $this->client->accounts->createExternalAccount(
config('variables.stripe_account'), [
'external_account' => [
'object' => 'bank_account',
'country' => $request->input('bank_country'),
'currency' => $request->input('bank_currency'),
'account_number' => $request->input('bank_account_number'),
]
]
);
}
if($destination){
$transaction->update([
'payment_method_exact' => $destination->id
]);
// Create payout in Stripe
// https://stripe.com/docs/api/payouts/create
$payout = $this->client->payouts->create([
"amount" => stripeFormatCurrency($amount, $currency),
"currency" => $currency,
'description' => $request->input('reason'),
"destination" => $destination->id,
"method" => $payment_method == 'CARD' ? 'instant' : 'standard'
]);
if($payout) {
$transaction->update([
'payment_date' => date('Y-m-d H:i:s', $payout->arrival_date),
'aggregator_payment_ref' => $payout->id,
'status' => strtolower($payout->status)
]);
return $this->successResponse([
'transaction_id' => $transaction->transaction_id,
'status' => $transaction->status
]);
}
}
}catch (Throwable $e){
Log::error("Error Stripe submit payment intent");
$errorMessage = $e->getMessage();
Log::error($errorMessage);
}
return $this->errorResponse($errorMessage ?? __('errors.unexpected_error'));
}
public function capturePaymentResult(Request $request)
{
$this->validate($request, [
'payment_intent' => 'nullable|string',
'transaction_id' => 'required|string|exists:payment_transactions,transaction_id',
]);
$transaction = PaymentTransaction::where('transaction_id',$request->input('transaction_id'))->firstOrFail();
if($request->has('payment_intent')) {
$paymentIntent = $this->client->paymentIntents->retrieve($request->input('payment_intent'));
return $this->handlePaymentIntentResult($transaction, $paymentIntent);
}
return $this->errorResponse(__('errors.unexpected_error'));
}
private function handlePaymentIntentResult(PaymentTransaction $transaction, PaymentIntent $intent){
if($intent->status == 'succeeded'){
$transaction->update([
'status' => PaymentTransactionStatus::ACCEPTED
]);
2023-06-14 14:49:24 +00:00
return redirect()->route('paymentResult',[
'message' => 'Payment Accepted',
'transaction_id' => $transaction->transaction_id,
2023-06-14 14:49:24 +00:00
'status' => 1
]);
}else{
$transaction->update([
'status' => strtolower($intent->status)
]);
2023-06-14 14:49:24 +00:00
return redirect()->route('paymentResult', [
'message' => "Payment ".$transaction->status,
'status' => 0
]);
}
}
2023-05-30 01:37:16 +00:00
}