Implement Stripe PayIn , Payout and Refund

This commit is contained in:
Djery-Tom 2023-06-07 20:56:04 +01:00
parent eaca44fa96
commit 329edb354d
16 changed files with 848 additions and 52 deletions

View File

@ -42,3 +42,4 @@ RECEIVER_NAME="Commune X"
STRIPE_KEY=pk_test_51NAILHJ6IfmAiBwqd8t8ZL9WjTdcMOSDt46TfLT1DS1VPRTrEY0UC3RDUF0b0woE95FkiUt84JVfIXgHCco4v9MO00DcQ7LkmO
STRIPE_SECRET=sk_test_51NAILHJ6IfmAiBwqgblKnBatWzIt3mtMYyw9Tc2RRFWUJJDVJ2VGKCBo3o0eTPCAigLB8lAbPiDiuvQ9Arwg0fad00fv7zIJdY
STRIPE_WEBHOOK_SECRET=your-stripe-webhook-secret
STRIPE_ACCOUNT=acct_1NAILHJ6IfmAiBwq

View File

@ -4,7 +4,7 @@
namespace App\Enums;
abstract class PaymentTransactionState
abstract class PaymentTransactionStatus
{
const INITIATED = 'INITIATED';
const ACCEPTED = 'ACCEPTED';

View File

@ -2,7 +2,7 @@
namespace App\Http\Controllers;
use App\Enums\PaymentTransactionState;
use App\Enums\PaymentTransactionStatus;
use App\Models\PaymentTransaction;
use GuzzleHttp\Client;
use Illuminate\Http\Request;
@ -78,7 +78,7 @@ class CinetpayController extends Controller
'payment_method' => 'nullable|string|in:ALL,MOBILE_MONEY,CREDIT_CARD,WALLET',
'customer_id' => 'required|integer',
'customer_email' => 'required|email',
'customer_name' => 'required|string',
'customer_name' => 'nullable|string',
'customer_surname' => 'required|string',
'customer_phone_number' => 'required|string',
'customer_address' => 'required|string',
@ -140,7 +140,7 @@ class CinetpayController extends Controller
"payment_method" => $payment_method,
"payment_token" => $responseData->data->payment_token,
"payment_url" => $responseData->data->payment_url,
'state' => PaymentTransactionState::PENDING,
'status' => PaymentTransactionStatus::PENDING,
"reason" => $request->input('reason'),
"customer_id" => $request->input('customer_id'),
"customer_name" => $request->input('customer_name'),
@ -213,7 +213,7 @@ class CinetpayController extends Controller
if ($responseCode == 200) {
$transaction->update([
'state' => $responseData->data->status,
'status' => $responseData->data->status,
'payment_method_exact' => $responseData->data->payment_method ?? null,
'aggregator_payment_ref' => $responseData->data->operator_id ?? null,
'payment_date' => $responseData->data->payment_date ?? null,
@ -225,12 +225,12 @@ class CinetpayController extends Controller
Log::info("Get Payment Status Error");
Log::info($e->getMessage());
$transaction->update([
'state' => PaymentTransactionState::REFUSED
'status' => PaymentTransactionStatus::REFUSED
]);
}
if($transaction->state == PaymentTransactionState::ACCEPTED){
if($transaction->status == PaymentTransactionStatus::ACCEPTED){
return redirect()->route('paymentResult',[
'transaction_id' => $transaction->transaction_id,
'token' => $transaction->payment_token,

View File

@ -2,7 +2,7 @@
namespace App\Http\Controllers;
use App\Enums\PaymentTransactionState;
use App\Enums\PaymentTransactionStatus;
use App\Models\PaymentAggregator;
use App\Models\PaymentTransaction;
use GuzzleHttp\Client;
@ -87,17 +87,39 @@ class PaymentController extends Controller
$receiver_logo = asset('assets/images/logo.jpeg');
if($transaction->state == PaymentTransactionState::INITIATED){
if($transaction->status == PaymentTransactionStatus::INITIATED){
return view('checkout',compact('payment_token','method','amount', 'receiver','receiver_logo'));
}
if($transaction->state == PaymentTransactionState::PENDING){
if($transaction->status == PaymentTransactionStatus::PENDING){
return view('verify-payment',compact('transaction_id','method','amount', 'receiver','receiver_logo'));
}
$status = $transaction->state == PaymentTransactionState::ACCEPTED;
$status = $transaction->status == PaymentTransactionStatus::ACCEPTED;
return view('payment-status',compact('transaction_id','method','amount', 'receiver','receiver_logo','status'));
}
public function checkStatus(Request $request, $transaction_id)
{
$transaction = PaymentTransaction::where('transaction_id',$transaction_id)->firstOrFail();
if($transaction->status == PaymentTransactionStatus::ACCEPTED){
return $this->successResponse([
'message' => 'Payment Accepted',
'transaction_id' => $transaction->transaction_id,
'status' => $transaction->status
]);
}else{
return $this->errorResponse([
'message' => 'Payment '.$transaction->status,
'status' => $transaction->status
]);
}
}
}

View File

@ -2,14 +2,16 @@
namespace App\Http\Controllers;
use App\Enums\PaymentTransactionState;
use App\Enums\PaymentTransactionStatus;
use App\Models\PaymentAggregator;
use App\Models\PaymentRefund;
use App\Models\PaymentTransaction;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Lang;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Str;
use Stripe\PaymentIntent;
use Stripe\StripeClient;
use Throwable;
@ -62,22 +64,22 @@ class StripeController extends Controller
'aggregator_id' => 'required|integer',
'amount' => 'required|numeric|min:5',
'currency' => 'required|string|size:3',
'customer_id' => 'required|integer',
'customer_id' => 'nullable',
'payment_method' => 'nullable|string',
'customer_email' => 'required|email',
'customer_name' => 'required|string',
'customer_name' => 'nullable|string',
'customer_surname' => 'required|string',
'customer_phone_number' => 'required|string',
'customer_phone_number' => 'nullable|string',
'customer_address' => 'required|string',
'customer_city' => 'required_if:payment_method,CREDIT_CARD|string',
'customer_city' => 'required_if:payment_method,CARD|string',
'customer_country' => 'required|string|size:2',
'customer_state' => 'required_if:payment_method,CREDIT_CARD|string|size:2', //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,CREDIT_CARD|string|size:5',
'customer_state' => 'required_if:payment_method,CARD|string|size:2', //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',
'reason' => 'required|string'
]);
$transaction_id = $this->getTransactionID();
$payment_method = $request->input('payment_method','CREDIT_CARD');
$payment_method = $request->input('payment_method','CARD');
$amount = $request->input('amount');
$currency = $request->input('currency');
@ -100,7 +102,7 @@ class StripeController extends Controller
"payment_method" => $payment_method,
"payment_token" => $payment_token,
"payment_url" => $payment_url,
'state' => PaymentTransactionState::INITIATED,
'status' => PaymentTransactionStatus::INITIATED,
"reason" => $request->input('reason'),
"customer_id" => $request->input('customer_id'),
"customer_name" => $request->input('customer_name'),
@ -136,7 +138,7 @@ class StripeController extends Controller
]);
$token = $request->input('payment_token');
$transaction = PaymentTransaction::where('payment_token', $token)->where('state', PaymentTransactionState::INITIATED)->firstOrFail();
$transaction = PaymentTransaction::where('payment_token', $token)->where('status', PaymentTransactionStatus::INITIATED)->firstOrFail();
// Init payment
@ -168,8 +170,8 @@ class StripeController extends Controller
$transaction->update([
'aggregator_payment_ref' => $charge->id,
'payment_date' => date('Y-m-d H:i:s', $charge->created) ,
'state' => PaymentTransactionState::ACCEPTED,
'payment_date' => date('Y-m-d H:i:s', $charge->created),
'status' => PaymentTransactionStatus::ACCEPTED,
'payment_method_exact' => $request->input('card_number')
]);
@ -189,4 +191,353 @@ class StripeController extends Controller
} 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' => 'required_if:customer_country,US|string|size:2', //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:customer_country,US|string|size:5',
'reason' => 'required|string'
]);
$aggregator = PaymentAggregator::where('name','like','%stripe%')->firstOrFail();
try{
$amount = $request->input('amount');
$currency = $request->input('currency');
if ($currency != 'USD') {
// Convertir en multiple de 5
$amount = $this->roundUpToAny($amount);
}
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" => $amount,
"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',
'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' => 'required_if:customer_country,US|string|size:2', //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:customer_country,US|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'),
'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" => $amount,
"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
]);
return $this->successResponse([
'message' => 'Payment Accepted',
'transaction_id' => $transaction->transaction_id,
'status' => $transaction->status
]);
}else{
$transaction->update([
'status' => strtolower($intent->status)
]);
return $this->errorResponse([
'message' => 'Payment '.$intent->status,
'status' => $transaction->status
]);
}
}
}

View File

@ -2,7 +2,7 @@
namespace App\Http\Controllers;
use App\Enums\PaymentTransactionState;
use App\Enums\PaymentTransactionStatus;
use App\Models\PaymentTransaction;
use GuzzleHttp\Client;
use Illuminate\Http\Request;
@ -76,7 +76,7 @@ class YoomeeController extends Controller
'payment_method' => 'required|string',
'customer_id' => 'required|integer',
'customer_email' => 'required|email',
'customer_name' => 'required|string',
'customer_name' => 'nullable|string',
'customer_surname' => 'required|string',
'customer_phone_number' => 'required|string',
'customer_address' => 'required|string',
@ -113,7 +113,7 @@ class YoomeeController extends Controller
"amount" => $createResponse->transactionAmount,
"payment_method" => $payment_method,
'payment_token' => Str::random(32),
'state' => $createResponse->paymentStatus,
'status' => $createResponse->paymentStatus,
"reason" => $request->input('reason'),
"customer_id" => $request->input('customer_id'),
"customer_name" => $request->input('customer_name'),
@ -147,7 +147,7 @@ class YoomeeController extends Controller
if ($responseCode == 202) {
$transaction->update([
'state' => PaymentTransactionState::ACCEPTED,
'status' => PaymentTransactionStatus::ACCEPTED,
'aggregator_payment_ref' => $this->getTransactionRef($responseData->message),
]);
return $this->successResponse([

View File

@ -2,7 +2,7 @@
namespace App\Http\Controllers;
use App\Enums\PaymentTransactionState;
use App\Enums\PaymentTransactionStatus;
use App\Models\PaymentTransaction;
use GuzzleHttp\Client;
use Illuminate\Http\Request;
@ -86,7 +86,7 @@ class YoomeeV2Controller extends Controller
'payment_method' => 'required|string',
'customer_id' => 'required|integer',
'customer_email' => 'required|email',
'customer_name' => 'required|string',
'customer_name' => 'nullable|string',
'customer_surname' => 'required|string',
'customer_phone_number' => 'required|string',
'customer_address' => 'required|string',
@ -115,7 +115,7 @@ class YoomeeV2Controller extends Controller
"payment_method" => $payment_method,
"payment_url" => $payment_url,
'payment_token' => $payment_token,
'state' => PaymentTransactionState::INITIATED,
'status' => PaymentTransactionStatus::INITIATED,
"reason" => $request->input('reason'),
"customer_id" => $request->input('customer_id'),
"customer_name" => $request->input('customer_name'),
@ -144,7 +144,7 @@ class YoomeeV2Controller extends Controller
$token = $request->input('payment_token');
$transaction = PaymentTransaction::where('payment_token', $token)
->where('state', PaymentTransactionState::INITIATED)->firstOrFail();
->where('status', PaymentTransactionStatus::INITIATED)->firstOrFail();
$customer_phone_number = $request->input('phone_number');
@ -187,7 +187,7 @@ class YoomeeV2Controller extends Controller
$transaction->update([
'aggregator_payment_ref' => $createResponse->transaction_number,
'payment_date' => $createResponse->transaction_date,
'state' => strtoupper($createResponse->transaction_status),
'status' => strtoupper($createResponse->transaction_status),
"customer_phone_number" => $customer_phone_number,
]);
@ -208,7 +208,7 @@ class YoomeeV2Controller extends Controller
'payment_method' => 'required|string',
'customer_id' => 'required|integer',
'customer_email' => 'required|email',
'customer_name' => 'required|string',
'customer_name' => 'nullable|string',
'customer_surname' => 'required|string',
'customer_phone_number' => 'required|string',
'customer_address' => 'required|string',
@ -256,7 +256,7 @@ class YoomeeV2Controller extends Controller
'payment_date' => $createResponse->transaction_date,
"payment_method" => $payment_method,
'payment_token' => Str::random(64),
'state' => strtoupper($createResponse->transaction_status),
'status' => strtoupper($createResponse->transaction_status),
"reason" => $request->input('reason'),
"customer_id" => $request->input('customer_id'),
"customer_name" => $request->input('customer_name'),
@ -270,7 +270,7 @@ class YoomeeV2Controller extends Controller
"customer_zip_code" => $request->input('customer_zip_code'),
]);
if($transaction->state == PaymentTransactionState::PENDING){
if($transaction->status == PaymentTransactionStatus::PENDING){
return $this->successResponse([
'verification_url' => route('yoomee.v2.webhook',['transaction_id' => $transaction_id])
]);
@ -309,9 +309,9 @@ class YoomeeV2Controller extends Controller
try {
// Si le paiement fait plus de 5 min on l'annule
if($transaction->state == PaymentTransactionState::PENDING && $transaction->created_at->diffInMinutes(Carbon::now()) > 5){
if($transaction->status == PaymentTransactionStatus::PENDING && $transaction->created_at->diffInMinutes(Carbon::now()) > 5){
$transaction->update([
'state' => PaymentTransactionState::CANCELLED
'status' => PaymentTransactionStatus::CANCELLED
]);
}
$response = $this->client->post('status/v1', [
@ -330,17 +330,17 @@ class YoomeeV2Controller extends Controller
if ($responseCode == 400) {
$state = strtoupper($responseData->transaction_status);
if($state == 'SUCCESS'){
$state = PaymentTransactionState::ACCEPTED;
$status = strtoupper($responseData->transaction_status);
if($status == 'SUCCESS'){
$status = PaymentTransactionStatus::ACCEPTED;
}else{
if(str_starts_with($state,'C')){
$state = PaymentTransactionState::REFUSED;
if(str_starts_with($status,'C')){
$status = PaymentTransactionStatus::REFUSED;
}
}
$transaction->update([
'state' => $state,
'status' => $status,
'payment_date' => $responseData->transaction_date ?? null,
]);
}
@ -349,7 +349,7 @@ class YoomeeV2Controller extends Controller
Log::info("Get Yoomee Payment Status Error");
Log::info($e->getMessage());
$transaction->update([
'state' => PaymentTransactionState::REFUSED
'status' => PaymentTransactionStatus::REFUSED
]);
}
@ -357,7 +357,7 @@ class YoomeeV2Controller extends Controller
if($verify_btn){
return redirect()->route('checkout',['payment_token' => $transaction->payment_token]);
}else {
if ($transaction->state == PaymentTransactionState::ACCEPTED) {
if ($transaction->status == PaymentTransactionStatus::ACCEPTED) {
return [
'message' => "Payment accepted",
'status' => 1,
@ -383,7 +383,7 @@ class YoomeeV2Controller extends Controller
$transaction = PaymentTransaction::where('transaction_id',$request->input('transaction_id'))->first();
if ($transaction->state == PaymentTransactionState::ACCEPTED) {
if ($transaction->status == PaymentTransactionStatus::ACCEPTED) {
return redirect()->route('paymentResult', [
'transaction_id' => $transaction->transaction_id,
'token' => $transaction->payment_token,

View File

@ -0,0 +1,11 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class PaymentRefund extends Model
{
protected $table = 'payment_refunds';
protected $guarded = ['id'];
}

View File

@ -7,11 +7,11 @@ use Illuminate\Support\Facades\DB;
trait Helper
{
public function getTransactionID()
public function getTransactionID($table = 'payment_transactions')
{
do {
$code = randomString(8);
$result = collect(DB::select('SELECT * FROM payment_transactions WHERE transaction_id = :code', ['code' => $code]));
$result = collect(DB::select("SELECT * FROM $table WHERE transaction_id = :code", ['code' => $code]));
$codeCorrect = sizeof($result) < 0;
} while ($codeCorrect);
return $code;

View File

@ -9,6 +9,7 @@
"ext-json": "*",
"cknow/laravel-money": "^7.0",
"darkaonline/swagger-lume": "^9.0",
"doctrine/dbal": "^3.6",
"flipbox/lumen-generator": "^9.1",
"guzzlehttp/guzzle": "^7.4",
"illuminate/session": "^9.52",

299
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "415cc3a989344fb30c3fecd2cdff4999",
"content-hash": "f0ebadd8749412a4cd14d3ae29e1d742",
"packages": [
{
"name": "brick/math",
@ -339,6 +339,211 @@
},
"time": "2023-02-01T09:20:38+00:00"
},
{
"name": "doctrine/cache",
"version": "2.2.0",
"source": {
"type": "git",
"url": "https://github.com/doctrine/cache.git",
"reference": "1ca8f21980e770095a31456042471a57bc4c68fb"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/doctrine/cache/zipball/1ca8f21980e770095a31456042471a57bc4c68fb",
"reference": "1ca8f21980e770095a31456042471a57bc4c68fb",
"shasum": ""
},
"require": {
"php": "~7.1 || ^8.0"
},
"conflict": {
"doctrine/common": ">2.2,<2.4"
},
"require-dev": {
"cache/integration-tests": "dev-master",
"doctrine/coding-standard": "^9",
"phpunit/phpunit": "^7.5 || ^8.5 || ^9.5",
"psr/cache": "^1.0 || ^2.0 || ^3.0",
"symfony/cache": "^4.4 || ^5.4 || ^6",
"symfony/var-exporter": "^4.4 || ^5.4 || ^6"
},
"type": "library",
"autoload": {
"psr-4": {
"Doctrine\\Common\\Cache\\": "lib/Doctrine/Common/Cache"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Guilherme Blanco",
"email": "guilhermeblanco@gmail.com"
},
{
"name": "Roman Borschel",
"email": "roman@code-factory.org"
},
{
"name": "Benjamin Eberlei",
"email": "kontakt@beberlei.de"
},
{
"name": "Jonathan Wage",
"email": "jonwage@gmail.com"
},
{
"name": "Johannes Schmitt",
"email": "schmittjoh@gmail.com"
}
],
"description": "PHP Doctrine Cache library is a popular cache implementation that supports many different drivers such as redis, memcache, apc, mongodb and others.",
"homepage": "https://www.doctrine-project.org/projects/cache.html",
"keywords": [
"abstraction",
"apcu",
"cache",
"caching",
"couchdb",
"memcached",
"php",
"redis",
"xcache"
],
"support": {
"issues": "https://github.com/doctrine/cache/issues",
"source": "https://github.com/doctrine/cache/tree/2.2.0"
},
"funding": [
{
"url": "https://www.doctrine-project.org/sponsorship.html",
"type": "custom"
},
{
"url": "https://www.patreon.com/phpdoctrine",
"type": "patreon"
},
{
"url": "https://tidelift.com/funding/github/packagist/doctrine%2Fcache",
"type": "tidelift"
}
],
"time": "2022-05-20T20:07:39+00:00"
},
{
"name": "doctrine/dbal",
"version": "3.6.3",
"source": {
"type": "git",
"url": "https://github.com/doctrine/dbal.git",
"reference": "9a747d29e7e6b39509b8f1847e37a23a0163ea6a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/doctrine/dbal/zipball/9a747d29e7e6b39509b8f1847e37a23a0163ea6a",
"reference": "9a747d29e7e6b39509b8f1847e37a23a0163ea6a",
"shasum": ""
},
"require": {
"composer-runtime-api": "^2",
"doctrine/cache": "^1.11|^2.0",
"doctrine/deprecations": "^0.5.3|^1",
"doctrine/event-manager": "^1|^2",
"php": "^7.4 || ^8.0",
"psr/cache": "^1|^2|^3",
"psr/log": "^1|^2|^3"
},
"require-dev": {
"doctrine/coding-standard": "12.0.0",
"fig/log-test": "^1",
"jetbrains/phpstorm-stubs": "2022.3",
"phpstan/phpstan": "1.10.14",
"phpstan/phpstan-strict-rules": "^1.5",
"phpunit/phpunit": "9.6.7",
"psalm/plugin-phpunit": "0.18.4",
"squizlabs/php_codesniffer": "3.7.2",
"symfony/cache": "^5.4|^6.0",
"symfony/console": "^4.4|^5.4|^6.0",
"vimeo/psalm": "4.30.0"
},
"suggest": {
"symfony/console": "For helpful console commands such as SQL execution and import of files."
},
"bin": [
"bin/doctrine-dbal"
],
"type": "library",
"autoload": {
"psr-4": {
"Doctrine\\DBAL\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Guilherme Blanco",
"email": "guilhermeblanco@gmail.com"
},
{
"name": "Roman Borschel",
"email": "roman@code-factory.org"
},
{
"name": "Benjamin Eberlei",
"email": "kontakt@beberlei.de"
},
{
"name": "Jonathan Wage",
"email": "jonwage@gmail.com"
}
],
"description": "Powerful PHP database abstraction layer (DBAL) with many features for database schema introspection and management.",
"homepage": "https://www.doctrine-project.org/projects/dbal.html",
"keywords": [
"abstraction",
"database",
"db2",
"dbal",
"mariadb",
"mssql",
"mysql",
"oci8",
"oracle",
"pdo",
"pgsql",
"postgresql",
"queryobject",
"sasql",
"sql",
"sqlite",
"sqlserver",
"sqlsrv"
],
"support": {
"issues": "https://github.com/doctrine/dbal/issues",
"source": "https://github.com/doctrine/dbal/tree/3.6.3"
},
"funding": [
{
"url": "https://www.doctrine-project.org/sponsorship.html",
"type": "custom"
},
{
"url": "https://www.patreon.com/phpdoctrine",
"type": "patreon"
},
{
"url": "https://tidelift.com/funding/github/packagist/doctrine%2Fdbal",
"type": "tidelift"
}
],
"time": "2023-06-01T05:46:46+00:00"
},
{
"name": "doctrine/deprecations",
"version": "v1.0.0",
@ -382,6 +587,98 @@
},
"time": "2022-05-02T15:47:09+00:00"
},
{
"name": "doctrine/event-manager",
"version": "1.2.0",
"source": {
"type": "git",
"url": "https://github.com/doctrine/event-manager.git",
"reference": "95aa4cb529f1e96576f3fda9f5705ada4056a520"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/doctrine/event-manager/zipball/95aa4cb529f1e96576f3fda9f5705ada4056a520",
"reference": "95aa4cb529f1e96576f3fda9f5705ada4056a520",
"shasum": ""
},
"require": {
"doctrine/deprecations": "^0.5.3 || ^1",
"php": "^7.1 || ^8.0"
},
"conflict": {
"doctrine/common": "<2.9"
},
"require-dev": {
"doctrine/coding-standard": "^9 || ^10",
"phpstan/phpstan": "~1.4.10 || ^1.8.8",
"phpunit/phpunit": "^7.5 || ^8.5 || ^9.5",
"vimeo/psalm": "^4.24"
},
"type": "library",
"autoload": {
"psr-4": {
"Doctrine\\Common\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Guilherme Blanco",
"email": "guilhermeblanco@gmail.com"
},
{
"name": "Roman Borschel",
"email": "roman@code-factory.org"
},
{
"name": "Benjamin Eberlei",
"email": "kontakt@beberlei.de"
},
{
"name": "Jonathan Wage",
"email": "jonwage@gmail.com"
},
{
"name": "Johannes Schmitt",
"email": "schmittjoh@gmail.com"
},
{
"name": "Marco Pivetta",
"email": "ocramius@gmail.com"
}
],
"description": "The Doctrine Event Manager is a simple PHP event system that was built to be used with the various Doctrine projects.",
"homepage": "https://www.doctrine-project.org/projects/event-manager.html",
"keywords": [
"event",
"event dispatcher",
"event manager",
"event system",
"events"
],
"support": {
"issues": "https://github.com/doctrine/event-manager/issues",
"source": "https://github.com/doctrine/event-manager/tree/1.2.0"
},
"funding": [
{
"url": "https://www.doctrine-project.org/sponsorship.html",
"type": "custom"
},
{
"url": "https://www.patreon.com/phpdoctrine",
"type": "patreon"
},
{
"url": "https://tidelift.com/funding/github/packagist/doctrine%2Fevent-manager",
"type": "tidelift"
}
],
"time": "2022-10-12T20:51:15+00:00"
},
{
"name": "doctrine/inflector",
"version": "2.0.6",

View File

@ -19,4 +19,5 @@ return [
'stripe_key' => env('STRIPE_KEY', ''),
'stripe_secret' => env('STRIPE_SECRET', ''),
'stripe_webhook_secret' => env('STRIPE_WEBHOOK_SECRET', ''),
'stripe_account' => env('STRIPE_ACCOUNT', ''),
];

View File

@ -0,0 +1,40 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('payment_refunds', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('refund_id')->unique();
$table->bigInteger('transaction_id');
$table->string('aggregator_payment_ref')->nullable();
$table->decimal('amount');
$table->string('currency',3);
$table->string('date')->nullable();
$table->text('reason');
$table->string('status');
$table->text('metadata')->nullable();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('payment_refunds');
}
};

View File

@ -0,0 +1,32 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('payment_transactions', function (Blueprint $table) {
$table->renameColumn('state','status');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('payment_transactions', function (Blueprint $table) {
$table->renameColumn('status','state');
});
}
};

View File

@ -0,0 +1,36 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('payment_transactions', function (Blueprint $table) {
$table->string('customer_id')->nullable()->change();
$table->string('customer_phone_number')->nullable()->change();
$table->string('customer_name')->nullable()->change();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('payment_transactions', function (Blueprint $table) {
$table->string('customer_id')->change();
$table->string('customer_phone_number')->change();
$table->string('customer_name')->change();
});
}
};

View File

@ -39,7 +39,7 @@ $router->group(['middleware' => 'session'], function () use ($router) {
*/
$router->addRoute(['GET','POST'],'/yoomee/v2/webhook', ['as' => 'yoomee.v2.webhook' , 'uses' => 'YoomeeV2Controller@capturePaymentResult']);
$router->addRoute(['GET','POST'],'/cinetpay/webhook', ['as' => 'cinetpay.webhook' , 'uses' => 'CinetpayController@capturePaymentResult']);
$router->addRoute(['GET','POST'],'/stripe/webhook', ['as' => 'cinetpay.webhook' , 'uses' => 'CinetpayController@capturePaymentResult']);
$router->addRoute(['GET','POST'],'/stripe/webhook', ['as' => 'stripe.webhook' , 'uses' => 'StripeController@capturePaymentResult']);
$router->addRoute(['GET','POST'],'/paymentResult', ['as' => 'paymentResult' , 'uses' => 'PaymentController@paymentResult']);
@ -50,6 +50,7 @@ $router->group(['middleware' => 'auth'], function () use ($router) {
*/
$router->get('methods','PaymentController@getMethods');
$router->post('pay','PaymentController@pay');
$router->get('checkStatus/{transaction_id}','PaymentController@checkStatus');
/**
@ -77,8 +78,11 @@ $router->group(['middleware' => 'auth'], function () use ($router) {
* Stripe Endpoints
*/
$router->group(['prefix' => 'stripe'], function () use ($router) {
$router->post('refund',['as' => 'stripe.refund', 'uses' => 'StripeController@refund']);
$router->get('methods',['as' => 'stripe.methods', 'uses' => 'StripeController@getMethods']);
$router->addRoute(['POST'],'pay',['as' => 'stripe.pay', 'uses' => 'StripeController@pay']);
$router->post('pay',['as' => 'stripe.pay', 'uses' => 'StripeController@pay']);
$router->post('payIn',['as' => 'stripe.submit', 'uses' => 'StripeController@payIn']);
$router->post('payOut',['as' => 'stripe.submit', 'uses' => 'StripeController@payOut']);
});
});