classify admin
This commit is contained in:
141
app/Services/Payment/FlutterWavePayment.php
Normal file
141
app/Services/Payment/FlutterWavePayment.php
Normal file
@@ -0,0 +1,141 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Payment;
|
||||
|
||||
use App\Services\ResponseService;
|
||||
use KingFlamez\Rave\Rave as Flutterwave;
|
||||
use Exception;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
|
||||
|
||||
class FlutterwavePayment implements PaymentInterface
|
||||
{
|
||||
private string $currencyCode;
|
||||
private Flutterwave $flutterwave;
|
||||
private string $encryptionKey;
|
||||
protected $baseUrl;
|
||||
private string $secretKey;
|
||||
|
||||
public function __construct($secret_key, $public_key, $encryption_key, $currencyCode)
|
||||
{
|
||||
$this->currencyCode = $currencyCode;
|
||||
$this->encryptionKey = $encryption_key;
|
||||
$this->baseUrl = 'https://api.flutterwave.com/v3';
|
||||
$this->secretKey = $secret_key;
|
||||
// Initialize Flutterwave SDK with API keys
|
||||
$this->flutterwave = new Flutterwave([
|
||||
'publicKey' => $public_key,
|
||||
'secretKey' => $secret_key,
|
||||
'encryptionKey' => $encryption_key,
|
||||
]);
|
||||
}
|
||||
|
||||
public function createPaymentIntent($amount, $customMetaData)
|
||||
{
|
||||
try {
|
||||
if (empty($customMetaData['email'])) {
|
||||
throw new Exception("Email cannot be empty");
|
||||
}
|
||||
$redirectUrl = ($customMetaData['platform_type'] == 'app')
|
||||
? route('flutterwave.success')
|
||||
: route('flutterwave.success.web');
|
||||
|
||||
$finalAmount =$amount;
|
||||
// $transactionRef = uniqid('flw_');
|
||||
$transactionRef = 't' .'-'. $customMetaData['payment_transaction_id'] .'-'. 'p' .'-'. $customMetaData['package_id'];
|
||||
$data = [
|
||||
'tx_ref' => $transactionRef,
|
||||
'amount' => $finalAmount,
|
||||
'currency' => $this->currencyCode,
|
||||
'redirect_url' => $redirectUrl,
|
||||
'payment_options' => 'card,banktransfer', // You can add more payment options
|
||||
'customer' => [
|
||||
'email' => $customMetaData['email'],
|
||||
'phonenumber' => $customMetaData['phone'] ?? Auth::user()->mobile,
|
||||
'name' => $customMetaData['name'] ?? Auth::user()->name,
|
||||
],
|
||||
'meta' => [
|
||||
'package_id' => $customMetaData['package_id'],
|
||||
'user_id' => $customMetaData['user_id'],
|
||||
]
|
||||
];
|
||||
$data = json_encode($data, JSON_UNESCAPED_SLASHES);
|
||||
$url = 'https://api.flutterwave.com/v3/payments';
|
||||
|
||||
$ch = curl_init();
|
||||
|
||||
curl_setopt($ch, CURLOPT_URL, $url);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_POST, true);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||
'Content-Type: application/json',
|
||||
'Authorization: Bearer ' . $this->secretKey
|
||||
]);
|
||||
$response = curl_exec($ch);
|
||||
if (curl_errno($ch)) {
|
||||
return ['error' => curl_error($ch)];
|
||||
}
|
||||
curl_close($ch);
|
||||
$payment = json_decode($response,true);
|
||||
return $this->formatPaymentIntent($transactionRef, $finalAmount,$this->currencyCode,'pending',$customMetaData,$payment);
|
||||
} catch (Exception $e) {
|
||||
return ResponseService::errorResponse("Payment failed: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public function createAndFormatPaymentIntent($amount, $customMetaData): array
|
||||
{
|
||||
return $this->createPaymentIntent($amount, $customMetaData);
|
||||
}
|
||||
|
||||
public function retrievePaymentIntent($transactionId): array
|
||||
{
|
||||
try {
|
||||
$response = $this->flutterwave->verifyTransaction($transactionId);
|
||||
if ($response['status'] === 'success') {
|
||||
return $this->formatPaymentIntent(
|
||||
$response['data']['tx_ref'],
|
||||
$response['data']['amount'],
|
||||
$response['data']['currency'],
|
||||
$response['data']['status'],
|
||||
[],
|
||||
$response
|
||||
);
|
||||
}
|
||||
throw new Exception("Error fetching payment status: " . $response['message']);
|
||||
} catch (Exception $e) {
|
||||
throw new Exception("Error verifying transaction: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function formatPaymentIntent($id, $amount, $currency, $status, $metadata, $paymentIntent): array
|
||||
{
|
||||
return [
|
||||
'id' => $id,
|
||||
'amount' => $amount,
|
||||
'currency' => $currency,
|
||||
'metadata' => $metadata,
|
||||
'status' => match ($status) {
|
||||
'successful' => 'succeeded',
|
||||
'pending' => 'pending',
|
||||
'failed' => 'failed',
|
||||
default => 'unknown'
|
||||
},
|
||||
'payment_gateway_response' => $paymentIntent
|
||||
];
|
||||
}
|
||||
|
||||
public function minimumAmountValidation($currency, $amount)
|
||||
{
|
||||
$minimumAmount = match ($currency) {
|
||||
'NGN' => 50, // 50 Naira
|
||||
default => 1.00
|
||||
};
|
||||
|
||||
return ($amount >= $minimumAmount) ? $amount : $minimumAmount;
|
||||
}
|
||||
}
|
||||
251
app/Services/Payment/PayPalPayment.php
Normal file
251
app/Services/Payment/PayPalPayment.php
Normal file
@@ -0,0 +1,251 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Payment;
|
||||
|
||||
use Exception;
|
||||
use GuzzleHttp\Client;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Throwable;
|
||||
|
||||
class PayPalPayment implements PaymentInterface
|
||||
{
|
||||
private string $clientId;
|
||||
private string $secretKey;
|
||||
private string $currencyCode;
|
||||
private string $paymentmode;
|
||||
private Client $http;
|
||||
private string $baseUrl;
|
||||
|
||||
|
||||
public function __construct($clientId, $secretKey, $currencyCode, $paymentmode)
|
||||
{
|
||||
$this->clientId = $clientId;
|
||||
$this->secretKey = $secretKey;
|
||||
$this->currencyCode = $currencyCode;
|
||||
$this->paymentmode = $paymentmode;
|
||||
|
||||
$this->http = new Client([
|
||||
'base_uri' => ($paymentmode == "UAT") ? 'https://api.sandbox.paypal.com' : 'https://api-m.paypal.com',
|
||||
'timeout' => 30,
|
||||
]);
|
||||
$this->baseUrl = ($paymentmode == "UAT")
|
||||
? 'https://api.sandbox.paypal.com'
|
||||
: 'https://api.paypal.com';
|
||||
|
||||
}
|
||||
|
||||
private function generateAccessToken(): string
|
||||
{
|
||||
$response = $this->http->post('/v1/oauth2/token', [
|
||||
'auth' => [$this->clientId, $this->secretKey],
|
||||
'form_params' => ['grant_type' => 'client_credentials'],
|
||||
'headers' => [
|
||||
'Accept' => 'application/json',
|
||||
'Accept-Language' => 'en_US'
|
||||
]
|
||||
]);
|
||||
|
||||
$data = json_decode((string)$response->getBody(), true);
|
||||
return $data['access_token'] ?? throw new Exception("Unable to generate PayPal token");
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a PayPal Order (Payment Intent equivalent)
|
||||
*/
|
||||
public function createPaymentIntent($amount, $customMetaData)
|
||||
{
|
||||
$amount = $this->minimumAmountValidation($this->currencyCode, $amount);
|
||||
$accessToken = $this->generateAccessToken();
|
||||
if($customMetaData['platform_type'] == 'app') {
|
||||
$callbackUrl = route('paypal.success') ;
|
||||
}else{
|
||||
$callbackUrl = route('paypal.success.web');
|
||||
}
|
||||
$metaData = 't' . '-' . $customMetaData['payment_transaction_id'] . '-' . 'p' . '-' . $customMetaData['package_id'];
|
||||
$response = $this->http->post('/v2/checkout/orders', [
|
||||
'headers' => [
|
||||
'Authorization' => "Bearer {$accessToken}",
|
||||
'Content-Type' => 'application/json'
|
||||
],
|
||||
'json' => [
|
||||
'intent' => 'CAPTURE',
|
||||
'purchase_units' => [[
|
||||
'amount' => [
|
||||
'currency_code' => $this->currencyCode,
|
||||
'value' => number_format($amount, 2, '.', '')
|
||||
],
|
||||
'custom_id' => $metaData,
|
||||
'description' => $customMetaData['description'] ?? null,
|
||||
]],
|
||||
'application_context' => [
|
||||
'return_url' => $callbackUrl,
|
||||
'cancel_url' => $callbackUrl,
|
||||
]
|
||||
]
|
||||
|
||||
]);
|
||||
|
||||
$data = json_decode((string)$response->getBody(), true);
|
||||
return $data;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* ✅ Matches PaymentInterface (like Stripe)
|
||||
*/
|
||||
public function createAndFormatPaymentIntent($amount, $customMetaData): array
|
||||
{
|
||||
$order = $this->createPaymentIntent($amount, $customMetaData);
|
||||
|
||||
// Extract approve link from PayPal response
|
||||
$approveLink = null;
|
||||
if (isset($order['links']) && is_array($order['links'])) {
|
||||
foreach ($order['links'] as $link) {
|
||||
if ($link['rel'] === 'approve') {
|
||||
$approveLink = $link['href'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$formatted = $this->formatPaymentIntent(
|
||||
$order['id'],
|
||||
$amount,
|
||||
$this->currencyCode,
|
||||
$order['status'] ?? 'CREATED',
|
||||
$customMetaData,
|
||||
$order
|
||||
);
|
||||
|
||||
// Add approval link for frontend
|
||||
$formatted['approval_url'] = $approveLink;
|
||||
|
||||
return $formatted;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* ✅ Retrieve order details (similar to Stripe’s retrievePaymentIntent)
|
||||
*/
|
||||
public function retrievePaymentIntent($paymentId): array
|
||||
{
|
||||
$accessToken = $this->generateAccessToken();
|
||||
|
||||
$response = $this->http->get("/v2/checkout/orders/{$paymentId}", [
|
||||
'headers' => [
|
||||
'Authorization' => "Bearer {$accessToken}",
|
||||
'Content-Type' => 'application/json'
|
||||
]
|
||||
]);
|
||||
|
||||
$data = json_decode((string)$response->getBody(), true);
|
||||
|
||||
return $this->formatPaymentIntent(
|
||||
$data['id'],
|
||||
$data['purchase_units'][0]['amount']['value'] ?? 0,
|
||||
$data['purchase_units'][0]['amount']['currency_code'] ?? $this->currencyCode,
|
||||
$data['status'],
|
||||
[],
|
||||
$data
|
||||
);
|
||||
}
|
||||
|
||||
public function capturePayment($orderId): array
|
||||
{
|
||||
$accessToken = $this->generateAccessToken();
|
||||
|
||||
$response = $this->http->post("/v2/checkout/orders/{$orderId}/capture", [
|
||||
'headers' => [
|
||||
'Authorization' => "Bearer {$accessToken}",
|
||||
'Content-Type' => 'application/json'
|
||||
]
|
||||
]);
|
||||
|
||||
$data = json_decode((string)$response->getBody(), true);
|
||||
|
||||
return $this->formatPaymentIntent(
|
||||
$data['id'] ?? $orderId,
|
||||
$data['purchase_units'][0]['payments']['captures'][0]['amount']['value'] ?? 0,
|
||||
$data['purchase_units'][0]['payments']['captures'][0]['amount']['currency_code'] ?? $this->currencyCode,
|
||||
$data['status'] ?? 'FAILED',
|
||||
[],
|
||||
$data
|
||||
);
|
||||
}
|
||||
|
||||
public function refund($captureId, $amount, $currency = null): array
|
||||
{
|
||||
$accessToken = $this->generateAccessToken();
|
||||
$currency = $currency ?? $this->currencyCode;
|
||||
|
||||
$response = $this->http->post("/v2/payments/captures/{$captureId}/refund", [
|
||||
'headers' => [
|
||||
'Authorization' => "Bearer {$accessToken}",
|
||||
'Content-Type' => 'application/json'
|
||||
],
|
||||
'json' => [
|
||||
'amount' => [
|
||||
'value' => number_format($amount, 2, '.', ''),
|
||||
'currency_code' => $currency
|
||||
]
|
||||
]
|
||||
]);
|
||||
|
||||
return json_decode((string)$response->getBody(), true);
|
||||
}
|
||||
|
||||
public function formatPaymentIntent($id, $amount, $currency, $status, $metadata, $paymentIntent): array
|
||||
{
|
||||
return [
|
||||
'id' => $id,
|
||||
'amount' => $amount,
|
||||
'currency' => $currency,
|
||||
'metadata' => $metadata,
|
||||
'status' => match (strtolower($status)) {
|
||||
"completed" => "succeed",
|
||||
"approved" => "pending",
|
||||
"created" => "pending",
|
||||
default => "failed",
|
||||
},
|
||||
'payment_gateway_response' => $paymentIntent
|
||||
];
|
||||
}
|
||||
|
||||
public function minimumAmountValidation($currency, $amount)
|
||||
{
|
||||
$minimumAmount = 0.50;
|
||||
return max($amount, $minimumAmount);
|
||||
}
|
||||
|
||||
public function verifyWebhookSignature(array $headers, string $payload, string $webhookId): bool
|
||||
{
|
||||
try {
|
||||
$accessToken = $this->generateAccessToken();
|
||||
if($this->baseUrl == 'https://api.sandbox.paypal.com'){
|
||||
return true;
|
||||
}
|
||||
$verification = Http::withToken($accessToken)
|
||||
->post($this->baseUrl . '/v1/notifications/verify-webhook-signature', [
|
||||
'auth_algo' => $headers['paypal-auth-algo'][0] ?? '',
|
||||
'cert_url' => $headers['paypal-cert-url'][0] ?? '',
|
||||
'transmission_id' => $headers['paypal-transmission-id'][0] ?? '',
|
||||
'transmission_sig' => $headers['paypal-transmission-sig'][0] ?? '',
|
||||
'transmission_time' => $headers['paypal-transmission-time'][0] ?? '',
|
||||
'webhook_id' => $webhookId,
|
||||
'webhook_event' => json_decode($payload, true),
|
||||
]);
|
||||
|
||||
if (!$verification->successful()) {
|
||||
Log::error('PayPal verifyWebhookSignature failed: ' . $verification->body());
|
||||
return false;
|
||||
}
|
||||
|
||||
return ($verification->json()['verification_status'] ?? '') === 'SUCCESS';
|
||||
} catch (Throwable $e) {
|
||||
Log::error('PayPal verifyWebhookSignature exception: ' . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
17
app/Services/Payment/PaymentInterface.php
Normal file
17
app/Services/Payment/PaymentInterface.php
Normal file
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Payment;
|
||||
|
||||
interface PaymentInterface {
|
||||
public function createPaymentIntent($amount, $customMetaData);
|
||||
|
||||
public function createAndFormatPaymentIntent($amount, $customMetaData): array;
|
||||
|
||||
public function retrievePaymentIntent($paymentId): array;
|
||||
|
||||
public function minimumAmountValidation($currency, $amount);
|
||||
|
||||
public function formatPaymentIntent($id, $amount, $currency, $status, $metadata, $paymentIntent): array;
|
||||
//
|
||||
// public function checkPayment(Order $order): PaymentStatus;
|
||||
}
|
||||
71
app/Services/Payment/PaymentService.php
Normal file
71
app/Services/Payment/PaymentService.php
Normal file
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Payment;
|
||||
|
||||
use App\Models\PaymentConfiguration;
|
||||
use InvalidArgumentException;
|
||||
|
||||
class PaymentService {
|
||||
/**
|
||||
* @param string $paymentGateway - Stripe
|
||||
* @return StripePayment
|
||||
*/
|
||||
public static function create(string $paymentGateway) {
|
||||
$paymentGateway = strtolower($paymentGateway);
|
||||
$payment = PaymentConfiguration::where(['payment_method' => $paymentGateway, 'status' => 1])->first();
|
||||
|
||||
if (!$payment) {
|
||||
throw new InvalidArgumentException('Invalid Payment Gateway.');
|
||||
}
|
||||
return match ($paymentGateway) {
|
||||
'stripe' => new StripePayment($payment->secret_key, $payment->currency_code),
|
||||
'paystack' => new PaystackPayment($payment->currency_code),
|
||||
'razorpay' => new RazorpayPayment($payment->secret_key, $payment->api_key, $payment->currency_code),
|
||||
'phonepe' => new PhonePePayment($payment->secret_key , $payment->api_key,$payment->additional_data_1,$payment->additional_data_2,$payment->payment_mode),
|
||||
'flutterwave' => new FlutterWavePayment($payment->secret_key, $payment->api_key, $payment->webhook_secret_key, $payment->currency_code),
|
||||
'paypal' => new PayPalPayment($payment->api_key, $payment->secret_key, $payment->currency_code,$payment->payment_mode),
|
||||
'google,apple' => null,
|
||||
// any other payment processor implementations
|
||||
default => throw new InvalidArgumentException('Invalid Payment Gateway.'),
|
||||
};
|
||||
}
|
||||
|
||||
/***
|
||||
* @param string $paymentGateway
|
||||
* @param $paymentIntentData
|
||||
* @return array
|
||||
* Stripe Payment Intent : https://stripe.com/docs/api/payment_intents/object
|
||||
*/
|
||||
// public static function formatPaymentIntent(string $paymentGateway, $paymentIntentData) {
|
||||
// $paymentGateway = strtolower($paymentGateway);
|
||||
// return match ($paymentGateway) {
|
||||
// 'stripe' => [
|
||||
// 'id' => $paymentIntentData->id,
|
||||
// 'amount' => $paymentIntentData->amount,
|
||||
// 'currency' => $paymentIntentData->currency,
|
||||
// 'metadata' => $paymentIntentData->metadata,
|
||||
// 'status' => match ($paymentIntentData->status) {
|
||||
// "canceled" => "failed",
|
||||
// "succeeded" => "succeed",
|
||||
// "processing", "requires_action", "requires_capture", "requires_confirmation", "requires_payment_method" => "pending",
|
||||
// },
|
||||
// 'payment_gateway_response' => $paymentIntentData
|
||||
// ],
|
||||
//
|
||||
// 'paystack' => [
|
||||
// 'id' => $paymentIntentData['data']['reference'],
|
||||
// 'amount' => $paymentIntentData->amount,
|
||||
// 'currency' => $paymentIntentData->currency,
|
||||
// 'metadata' => $paymentIntentData->metadata,
|
||||
// 'status' => match ($paymentIntentData['data']['status']) {
|
||||
// "abandoned" => "failed",
|
||||
// "succeed" => "succeed",
|
||||
// default => $paymentIntentData['data']['status'] ?? true
|
||||
// },
|
||||
// 'payment_gateway_response' => $paymentIntentData
|
||||
// ],
|
||||
// // any other payment processor implementations
|
||||
// default => $paymentIntentData,
|
||||
// };
|
||||
// }
|
||||
}
|
||||
132
app/Services/Payment/PaystackPayment.php
Normal file
132
app/Services/Payment/PaystackPayment.php
Normal file
@@ -0,0 +1,132 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Payment;
|
||||
|
||||
use RuntimeException;
|
||||
use Throwable;
|
||||
use Unicodeveloper\Paystack\Paystack;
|
||||
|
||||
class PaystackPayment extends Paystack implements PaymentInterface {
|
||||
private Paystack $paystack;
|
||||
private string $currencyCode;
|
||||
|
||||
/**
|
||||
* PaystackPayment constructor.
|
||||
* @param $currencyCode
|
||||
*/
|
||||
public function __construct($currencyCode) {
|
||||
// Call Paystack Class and Create Payment Intent
|
||||
$this->paystack = new Paystack();
|
||||
$this->currencyCode = $currencyCode;
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $amount
|
||||
* @param $customMetaData
|
||||
* @return array
|
||||
*/
|
||||
public function createPaymentIntent($amount, $customMetaData) {
|
||||
|
||||
try {
|
||||
|
||||
if (empty($customMetaData['email'])) {
|
||||
throw new RuntimeException("Email cannot be empty");
|
||||
}
|
||||
if($customMetaData['platform_type'] == 'app') {
|
||||
$callbackUrl = route('paystack.success') ;
|
||||
}else{
|
||||
$callbackUrl = route('paystack.success.web');
|
||||
}
|
||||
|
||||
$finalAmount = $amount * 100;
|
||||
$reference = $this->genTranxRef();
|
||||
|
||||
|
||||
$data = [
|
||||
'amount' => $finalAmount,
|
||||
'currency' => $this->currencyCode,
|
||||
'email' => $customMetaData['email'],
|
||||
'metadata' => $customMetaData,
|
||||
'reference' => $reference,
|
||||
'callback_url' => $callbackUrl
|
||||
];
|
||||
|
||||
return $this->paystack->getAuthorizationResponse($data);
|
||||
|
||||
} catch (Throwable $e) {
|
||||
throw new RuntimeException($e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $amount
|
||||
* @param $customMetaData
|
||||
* @return array
|
||||
*/
|
||||
public function createAndFormatPaymentIntent($amount, $customMetaData): array {
|
||||
$response = $this->createPaymentIntent($amount, $customMetaData);
|
||||
return $this->format($response, $amount, $this->currencyCode, $customMetaData);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $paymentId
|
||||
* @return array
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function retrievePaymentIntent($paymentId): array {
|
||||
try {
|
||||
$relativeUrl = "/transaction/verify/{$paymentId}";
|
||||
$this->response = $this->client->get($this->baseUrl . $relativeUrl, []);
|
||||
$response = json_decode($this->response->getBody(), true, 512, JSON_THROW_ON_ERROR);
|
||||
return $this->format($response['data'], $response['data']['amount'], $response['data']['currency'], $response['data']['metadata']);
|
||||
} catch (Throwable $e) {
|
||||
throw new RuntimeException($e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $currency
|
||||
* @param $amount
|
||||
*/
|
||||
public function minimumAmountValidation($currency, $amount) {
|
||||
// TODO: Implement minimumAmountValidation() method.
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $paymentIntent
|
||||
* @param $amount
|
||||
* @param $currencyCode
|
||||
* @param $metadata
|
||||
* @return array
|
||||
*/
|
||||
public function format($paymentIntent, $amount, $currencyCode, $metadata) {
|
||||
return $this->formatPaymentIntent($paymentIntent['data']['reference'], $amount, $currencyCode, $paymentIntent['status'], $metadata, $paymentIntent);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $id
|
||||
* @param $amount
|
||||
* @param $currency
|
||||
* @param $status
|
||||
* @param $metadata
|
||||
* @param $paymentIntent
|
||||
* @return array
|
||||
*/
|
||||
public function formatPaymentIntent($id, $amount, $currency, $status, $metadata, $paymentIntent): array {
|
||||
return [
|
||||
'id' => $id,
|
||||
'amount' => $amount,
|
||||
'currency' => $currency,
|
||||
'metadata' => $metadata,
|
||||
'status' => match ($status) {
|
||||
"abandoned" => "failed",
|
||||
"succeed" => "succeed",
|
||||
default => $status ?? true
|
||||
},
|
||||
'payment_gateway_response' => $paymentIntent
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
382
app/Services/Payment/PhonePePayment.php
Normal file
382
app/Services/Payment/PhonePePayment.php
Normal file
@@ -0,0 +1,382 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Payment;
|
||||
|
||||
use Auth;
|
||||
use PhonePe\PhonePe as PhonePeSDK;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Exception;
|
||||
|
||||
class PhonePePayment implements PaymentInterface
|
||||
{
|
||||
private string $clientId;
|
||||
private string $callbackUrl;
|
||||
private string $transactionId;
|
||||
private string $clientSecret;
|
||||
private string $clientVersion;
|
||||
private string $payment_mode;
|
||||
private string $merchantId;
|
||||
private string $pgUrl;
|
||||
|
||||
public function __construct($clientSecret, $clientId, $addtional_data_1,$addtional_data_2, $payment_mode)
|
||||
{
|
||||
// $this->clientId = 'TEST-CITYSURFONLINE_2508';
|
||||
$this->clientId = $clientId;
|
||||
$this->callbackUrl = url('/webhook/phonePe');
|
||||
$this->transactionId = uniqid();
|
||||
$this->clientSecret = $clientSecret;
|
||||
$this->clientVersion = $addtional_data_1;
|
||||
$this->payment_mode = $payment_mode;
|
||||
$this->merchantId = $addtional_data_2;
|
||||
$this->pgUrl = ($payment_mode == "UAT") ? "https://api-preprod.phonepe.com/apis/pg-sandbox" : "https://api.phonepe.com/apis/pg";
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Create payment intent for PhonePe
|
||||
*
|
||||
* @param $amount
|
||||
* @param $customMetaData
|
||||
* @return array
|
||||
* @throws Exception
|
||||
*/
|
||||
public function createPaymentIntent($amount, $customMetaData) {
|
||||
Log::info("PhonePe Payment custome", [
|
||||
'amount' => $amount,
|
||||
'customMetaData' => $customMetaData,
|
||||
]);
|
||||
$amount = $this->minimumAmountValidation('INR', $amount);
|
||||
$userMobile = Auth::user()->mobile;
|
||||
$metaData = 't' . '-' . $customMetaData['payment_transaction_id'] . '-' . 'p' . '-' . $customMetaData['package_id'];
|
||||
|
||||
if ($customMetaData['platform_type'] == 'web') {
|
||||
$transactionId = uniqid();
|
||||
$mode = $this->payment_mode;
|
||||
if ($mode === 'PROD') {
|
||||
$tokenUrl = 'https://api.phonepe.com/apis/identity-manager/v1/oauth/token';
|
||||
$orderUrl = 'https://api.phonepe.com/apis/pg/checkout/v2/sdk/order';
|
||||
$payUrl = 'https://api.phonepe.com/apis/pg/checkout/v2/pay';
|
||||
} else {
|
||||
$tokenUrl = 'https://api-preprod.phonepe.com/apis/pg-sandbox/v1/oauth/token';
|
||||
$orderUrl = 'https://api-preprod.phonepe.com/apis/pg-sandbox/checkout/v2/sdk/order';
|
||||
$payUrl = 'https://api-preprod.phonepe.com/apis/pg-sandbox/checkout/v2/pay';
|
||||
}
|
||||
$tokenCurl = curl_init();
|
||||
|
||||
curl_setopt_array($tokenCurl, array(
|
||||
CURLOPT_URL => $tokenUrl,
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_ENCODING => '',
|
||||
CURLOPT_MAXREDIRS => 10,
|
||||
CURLOPT_TIMEOUT => 0,
|
||||
CURLOPT_FOLLOWLOCATION => true,
|
||||
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
|
||||
CURLOPT_CUSTOMREQUEST => 'POST',
|
||||
CURLOPT_POSTFIELDS => http_build_query([
|
||||
'client_id' => $this->clientId,
|
||||
'client_version' => $this->clientVersion,
|
||||
'client_secret' => $this->clientSecret,
|
||||
'grant_type' => 'client_credentials',
|
||||
]),
|
||||
CURLOPT_HTTPHEADER => array(
|
||||
'Content-Type: application/x-www-form-urlencoded'
|
||||
),
|
||||
));
|
||||
|
||||
$response = curl_exec($tokenCurl);
|
||||
curl_close($tokenCurl);
|
||||
|
||||
// Decode token response
|
||||
$tokenResponse = json_decode($response, true);
|
||||
$accessToken = $tokenResponse['access_token'] ?? null;
|
||||
|
||||
if (!$accessToken) {
|
||||
dd("Failed to retrieve token", $response);
|
||||
}
|
||||
// Build JSON payload properly
|
||||
$paymentData = [
|
||||
'merchantOrderId' => $metaData,
|
||||
'amount' => (int) round($amount * 100),
|
||||
"metadata" => [
|
||||
"package_id" => $customMetaData['package_id'],
|
||||
"payment_transaction_id" => $customMetaData['payment_transaction_id'],
|
||||
"user_id" => Auth::user()->id,
|
||||
],
|
||||
'paymentFlow' => [
|
||||
'type' => 'PG_CHECKOUT',
|
||||
'message' => 'Payment message used for collect requests',
|
||||
'merchantUrls' => [
|
||||
'redirectUrl' => route('phonepe.success.web'),
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
$payCUrl = curl_init();
|
||||
|
||||
curl_setopt_array($payCUrl, array(
|
||||
CURLOPT_URL => $payUrl,
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_ENCODING => '',
|
||||
CURLOPT_MAXREDIRS => 10,
|
||||
CURLOPT_TIMEOUT => 0,
|
||||
CURLOPT_FOLLOWLOCATION => true,
|
||||
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
|
||||
CURLOPT_CUSTOMREQUEST => 'POST',
|
||||
CURLOPT_POSTFIELDS => json_encode($paymentData),
|
||||
CURLOPT_HTTPHEADER => array(
|
||||
'Content-Type: application/json',
|
||||
'Authorization: O-Bearer ' . $accessToken,
|
||||
),
|
||||
));
|
||||
|
||||
$response = curl_exec($payCUrl);
|
||||
$final_response = json_decode($response, true);
|
||||
if (!empty($final_response)) {
|
||||
|
||||
$redirectURL = $final_response['redirectUrl'];
|
||||
return $this->formatPaymentIntent($transactionId, $amount, 'INR', 'pending', $customMetaData, $redirectURL);
|
||||
}
|
||||
curl_close($payCUrl);
|
||||
} else {
|
||||
$redirectUrl = route('phonepe.success');
|
||||
$orderId = 'TX' . time(); // unique order ID
|
||||
$amount = (int) round($amount * 100); // amount in INR (not multiplied)
|
||||
$expireAfter = 1200; // in seconds (20 mins)
|
||||
$token = $this->getPhonePeToken();
|
||||
$order = $this->createOrder($token, $orderId, $amount);
|
||||
$order_data = json_decode($order, true);
|
||||
$requestPayload = [
|
||||
"orderId" => $order_data['orderId'],
|
||||
// "state" => "PENDING",
|
||||
'merchantOrderId' => $metaData,
|
||||
"merchantId" => $this->merchantId,
|
||||
"expireAT" => $expireAfter,
|
||||
"token" => $order_data['token'],
|
||||
"paymentMode" => [
|
||||
"type" => "PAY_PAGE"
|
||||
]
|
||||
];
|
||||
|
||||
// Convert to JSON string as required by Flutter SDK
|
||||
$requestString = json_encode($requestPayload);
|
||||
|
||||
if ($this->payment_mode == "UAT") {
|
||||
$payment_mode = "SANDBOX";
|
||||
} else {
|
||||
$payment_mode = "PRODUCTION";
|
||||
}
|
||||
|
||||
return [
|
||||
"environment" => $payment_mode, // or "PRODUCTION"
|
||||
"merchantId" => $this->merchantId,
|
||||
"flowId" => $orderId,
|
||||
"enableLogging" => true, // false in production
|
||||
"request" => $requestPayload,
|
||||
"appSchema" => "eclassify", // for iOS deep link return
|
||||
"token" => $token,
|
||||
];
|
||||
}
|
||||
throw new Exception("Error initiating payment: " . $redirectURL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and format payment intent for PhonePe
|
||||
*
|
||||
* @param $amount
|
||||
* @param $customMetaData
|
||||
* @return array
|
||||
* @throws Exception
|
||||
*/
|
||||
public function createAndFormatPaymentIntent($amount, $customMetaData): array
|
||||
{
|
||||
$paymentIntent = $this->createPaymentIntent($amount, $customMetaData);
|
||||
$metaData = 't' . '-' . $customMetaData['payment_transaction_id'] . '-' . 'p' . '-' . $customMetaData['package_id'];
|
||||
return $this->formatPaymentIntent(
|
||||
id: $metaData,
|
||||
amount: $amount,
|
||||
currency: 'INR',
|
||||
status: "PENDING",
|
||||
metadata: $customMetaData,
|
||||
paymentIntent: $paymentIntent
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve payment intent (check payment status)
|
||||
*
|
||||
* @param $transactionId
|
||||
* @return array
|
||||
* @throws Exception
|
||||
*/
|
||||
public function retrievePaymentIntent($transactionId): array
|
||||
{
|
||||
$statusUrl = 'https://api.phonepe.com/v3/transaction/' . $transactionId . '/status';
|
||||
$signature = $this->generateSignature(''); // Adjust if needed based on PhonePe requirements
|
||||
|
||||
$response = $this->sendRequest($statusUrl, '', $signature);
|
||||
|
||||
if ($response['success']) {
|
||||
return $this->formatPaymentIntent($transactionId, $response['amount'], 'INR', $response['status'], [], $response);
|
||||
}
|
||||
|
||||
throw new Exception("Error fetching payment status: " . $response['message']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Format payment intent response
|
||||
*
|
||||
* @param $id
|
||||
* @param $amount
|
||||
* @param $currency
|
||||
* @param $status
|
||||
* @param $metadata
|
||||
* @param $paymentIntent
|
||||
* @return array
|
||||
*/
|
||||
public function formatPaymentIntent($id, $amount, $currency, $status, $metadata, $paymentIntent): array
|
||||
{
|
||||
return [
|
||||
'id' => $id,
|
||||
'amount' => $amount,
|
||||
'currency' => $currency,
|
||||
'metadata' => $metadata,
|
||||
'status' => match ($status) {
|
||||
"SUCCESS" => "succeeded",
|
||||
"PENDING" => "pending",
|
||||
"FAILED" => "failed",
|
||||
default => "unknown"
|
||||
},
|
||||
'payment_gateway_response' => $paymentIntent
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Minimum amount validation
|
||||
*
|
||||
* @param $currency
|
||||
* @param $amount
|
||||
* @return float|int
|
||||
*/
|
||||
public function minimumAmountValidation($currency, $amount)
|
||||
{
|
||||
$minimumAmount = match ($currency) {
|
||||
'INR' => 1.00, // 1 Rupee
|
||||
default => 0.50
|
||||
};
|
||||
|
||||
return ($amount >= $minimumAmount) ? $amount : $minimumAmount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate HMAC signature for PhonePe
|
||||
*
|
||||
* @param $encodedRequestBody
|
||||
* @return string
|
||||
*/
|
||||
private function generateSignature($requestBody): string
|
||||
{
|
||||
// Concatenate raw JSON payload, endpoint, and salt key
|
||||
$stringToHash = $requestBody . '/pg/v1/pay' . $this->saltKey;
|
||||
|
||||
// Hash the string using SHA256
|
||||
$hash = hash('sha256', $stringToHash);
|
||||
|
||||
// Append salt index (Assumed to be 1 in this example)
|
||||
return $hash . '###' . 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send cURL request to PhonePe API
|
||||
*
|
||||
* @param $url
|
||||
* @param $requestBody
|
||||
* @param $signature
|
||||
* @return array
|
||||
*/
|
||||
// private function sendRequest($url, $requestBody, $signature): array
|
||||
// {
|
||||
// // dd($requestBody);
|
||||
// $ch = curl_init($url);
|
||||
// curl_setopt($ch, CURLOPT_POST, 1);
|
||||
// curl_setopt($ch, CURLOPT_POSTFIELDS, $requestBody);
|
||||
// curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||
// 'Content-Type: application/json',
|
||||
// 'X-VERIFY: ' . $signature,
|
||||
// ]);
|
||||
// curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
|
||||
// $response = curl_exec($ch);
|
||||
// curl_close($ch);
|
||||
// return json_decode($response, true);
|
||||
// }
|
||||
|
||||
public function getPhonePeToken() {
|
||||
$clientId = $this->clientId;
|
||||
$clientSecret = $this->clientSecret;
|
||||
$clientVersion = $this->clientVersion;
|
||||
|
||||
$postData = http_build_query([
|
||||
'client_id' => $clientId,
|
||||
'client_version' => $clientVersion,
|
||||
'client_secret' => $clientSecret,
|
||||
'grant_type' => 'client_credentials',
|
||||
]);
|
||||
|
||||
if ($this->payment_mode == "UAT") {
|
||||
$url = 'https://api-preprod.phonepe.com/apis/pg-sandbox/v1/oauth/token';
|
||||
} else {
|
||||
$url = 'https://api.phonepe.com/apis/identity-manager/v1/oauth/token';
|
||||
}
|
||||
$ch = curl_init();
|
||||
|
||||
curl_setopt($ch, CURLOPT_URL, $url);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_POST, true);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||
'Content-Type: application/x-www-form-urlencoded',
|
||||
]);
|
||||
|
||||
$response = curl_exec($ch);
|
||||
|
||||
if (curl_errno($ch)) {
|
||||
throw new \Exception('Curl error: ' . curl_error($ch));
|
||||
}
|
||||
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
curl_close($ch);
|
||||
|
||||
$responseData = json_decode($response, true);
|
||||
|
||||
if ($httpCode === 200 && isset($responseData['access_token'])) {
|
||||
return $responseData['access_token'];
|
||||
}
|
||||
|
||||
throw new \Exception('Failed to fetch PhonePe token. Response: ' . $response);
|
||||
}
|
||||
|
||||
public function createOrder($token, $merchantOrderId, $amount) {
|
||||
$url = $this->pgUrl . '/checkout/v2/sdk/order';
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_URL, $url);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_POST, true);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
|
||||
"merchantOrderId" => $merchantOrderId,
|
||||
"amount" => $amount,
|
||||
"paymentFlow" => [
|
||||
"type" => "PG_CHECKOUT"
|
||||
]
|
||||
]));
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||
'Content-Type: application/json',
|
||||
'Authorization: O-Bearer ' . $token,
|
||||
]);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
|
||||
$response = curl_exec($ch);
|
||||
curl_close($ch);
|
||||
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
119
app/Services/Payment/RazorpayPayment.php
Normal file
119
app/Services/Payment/RazorpayPayment.php
Normal file
@@ -0,0 +1,119 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Payment;
|
||||
|
||||
use Log;
|
||||
use Razorpay\Api\Api;
|
||||
use RuntimeException;
|
||||
use Throwable;
|
||||
|
||||
class RazorpayPayment implements PaymentInterface {
|
||||
private Api $api;
|
||||
private string $currencyCode;
|
||||
|
||||
/**
|
||||
* RazorpayPayment constructor.
|
||||
* @param $secretKey
|
||||
* @param $publicKey
|
||||
* @param $currencyCode
|
||||
*/
|
||||
public function __construct($secretKey, $publicKey, $currencyCode) {
|
||||
// Call Stripe Class and Create Payment Intent
|
||||
$this->api = new Api($publicKey, $secretKey);
|
||||
$this->currencyCode = $currencyCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $amount
|
||||
* @param $customMetaData
|
||||
* @return mixed
|
||||
*/
|
||||
public function createPaymentIntent($amount, $customMetaData) {
|
||||
try {
|
||||
$orderData = [
|
||||
'amount' => $this->minimumAmountValidation($this->currencyCode, $amount),
|
||||
'currency' => $this->currencyCode,
|
||||
'notes' => $customMetaData,
|
||||
];
|
||||
return $this->api->order->create($orderData);
|
||||
} catch (Throwable $e) {
|
||||
Log::error('Failed to create payment intent: ' . $e->getMessage());
|
||||
throw new RuntimeException($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $amount
|
||||
* @param $customMetaData
|
||||
* @return array
|
||||
*/
|
||||
public function createAndFormatPaymentIntent($amount, $customMetaData): array {
|
||||
$response = $this->createPaymentIntent($amount, $customMetaData);
|
||||
return $this->format($response);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $paymentId
|
||||
* @return array
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function retrievePaymentIntent($paymentId): array {
|
||||
try {
|
||||
return $this->api->order->fetch($paymentId);
|
||||
} catch (Throwable $e) {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $currency
|
||||
* @param $amount
|
||||
* @return float|int
|
||||
*/
|
||||
public function minimumAmountValidation($currency, $amount) {
|
||||
return match ($currency) {
|
||||
"BHD", "IQD", "JOD", "KWD", "OMR", "TND" => $amount * 1000,
|
||||
"AED", "ALL", "AMD", "ARS", "AUD", "AWG", "AZN", "BAM", "BBD", "BDT", "BGN", "BMD", "BND", "BOB", "BRL", "BSD", "BTN", "BWP", "BZD", "CAD", "CHF",
|
||||
"CNY", "COP", "CRC", "CUP", "CVE", "CZK", "DKK", "DOP", "DZD", "EGP", "ETB", "EUR", "FJD", "GBP", "GHS", "GIP", "GMD", "GTQ", "GYD", "HKD", "HNL",
|
||||
"HTG", "HUF", "IDR", "ILS", "INR", "JMD", "KES", "KGS", "KHR", "KYD", "KZT", "LAK", "LKR", "LRD", "LSL", "MAD", "MDL", "MGA", "MKD", "MMK", "MNT",
|
||||
"MOP", "MUR", "MVR", "MWK", "MXN", "MYR", "MZN", "NAD", "NGN", "NIO", "NOK", "NPR", "NZD", "PEN", "PGK", "PHP", "PKR", "PLN", "QAR", "RON", "RSD",
|
||||
"RUB", "SAR", "SCR", "SEK", "SGD", "SLL", "SOS", "SSP", "SVC", "SZL", "THB", "TTD", "TWD", "TZS", "UAH", "USD", "UYU", "UZS", "XCD", "YER", "ZAR", "ZMW" => $amount * 100,
|
||||
"BIF", "CLP", "DJF", "GNF", "ISK", "JPY", "KMF", "KRW", "PYG", "RWF", "UGX", "VND", "VUV", "XAF", "XOF", "XPF", "HRK" => $amount,
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $paymentIntent
|
||||
* @return array
|
||||
*/
|
||||
private function format($paymentIntent) {
|
||||
return $this->formatPaymentIntent($paymentIntent->id, $paymentIntent->amount, $paymentIntent->currency, $paymentIntent->status, $paymentIntent->notes->toArray(), $paymentIntent->toArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $id
|
||||
* @param $amount
|
||||
* @param $currency
|
||||
* @param $status
|
||||
* @param $metadata
|
||||
* @param $paymentIntent
|
||||
* @return array
|
||||
*/
|
||||
public function formatPaymentIntent($id, $amount, $currency, $status, $metadata, $paymentIntent): array {
|
||||
return [
|
||||
'id' => $id,
|
||||
'amount' => $amount,
|
||||
'currency' => $currency,
|
||||
'metadata' => $metadata,
|
||||
'status' => match ($status) {
|
||||
"failed" => "failed",//NOTE : Failed status is not known, please test the failure status
|
||||
"created", "attempted" => "pending",
|
||||
"paid" => "succeed",
|
||||
|
||||
},
|
||||
'payment_gateway_response' => $paymentIntent
|
||||
];
|
||||
}
|
||||
}
|
||||
184
app/Services/Payment/StripePayment.php
Normal file
184
app/Services/Payment/StripePayment.php
Normal file
@@ -0,0 +1,184 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Payment;
|
||||
|
||||
use App\Models\Setting;
|
||||
use App\Services\CurrencyFormatterService;
|
||||
use Stripe\Exception\ApiErrorException;
|
||||
use Stripe\PaymentIntent;
|
||||
use Stripe\StripeClient;
|
||||
|
||||
class StripePayment implements PaymentInterface {
|
||||
private StripeClient $stripe;
|
||||
private string $currencyCode;
|
||||
|
||||
/**
|
||||
* StripePayment constructor.
|
||||
* @param $secretKey
|
||||
* @param $currencyCode
|
||||
*/
|
||||
public function __construct($secretKey, $currencyCode) {
|
||||
// Call Stripe Class and Create Payment Intent
|
||||
$this->stripe = new StripeClient($secretKey);
|
||||
$this->currencyCode = $currencyCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $amount
|
||||
* @param $customMetaData
|
||||
* @return PaymentIntent
|
||||
* @throws ApiErrorException
|
||||
*/
|
||||
public function createPaymentIntent($amount, $customMetaData) {
|
||||
try {
|
||||
$amount = $this->minimumAmountValidation($this->currencyCode, $amount);
|
||||
$zeroDecimalCurrencies = [
|
||||
'BIF', 'CLP', 'DJF', 'GNF', 'JPY', 'KMF', 'KRW', 'MGA', 'PYG',
|
||||
'RWF', 'UGX', 'VND', 'VUV', 'XAF', 'XOF', 'XPF'
|
||||
];
|
||||
|
||||
// if (!in_array($this->currencyCode, $zeroDecimalCurrencies)) {
|
||||
// $amount *= 100;
|
||||
// }
|
||||
return $this->stripe->paymentIntents->create(
|
||||
[
|
||||
'amount' => $amount,
|
||||
'currency' => $this->currencyCode,
|
||||
'metadata' => $customMetaData,
|
||||
// 'description' => 'Fees Payment',
|
||||
// 'shipping' => [
|
||||
// 'name' => 'Jenny Rosen',
|
||||
// 'address' => [
|
||||
// 'line1' => '510 Townsend St',
|
||||
// 'postal_code' => '98140',
|
||||
// 'city' => 'San Francisco',
|
||||
// 'state' => 'CA',
|
||||
// 'country' => 'US',
|
||||
// ],
|
||||
// ],
|
||||
]
|
||||
);
|
||||
|
||||
} catch (ApiErrorException $e) {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $amount
|
||||
* @param $customMetaData
|
||||
* @return array
|
||||
* @throws ApiErrorException
|
||||
*/
|
||||
public function createAndFormatPaymentIntent($amount, $customMetaData): array {
|
||||
$paymentIntent = $this->createPaymentIntent($amount, $customMetaData);
|
||||
return $this->format($paymentIntent);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $paymentId
|
||||
* @return array
|
||||
* @throws ApiErrorException
|
||||
*/
|
||||
public function retrievePaymentIntent($paymentId): array {
|
||||
try {
|
||||
return $this->format($this->stripe->paymentIntents->retrieve($paymentId));
|
||||
} catch (ApiErrorException $e) {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $paymentIntent
|
||||
* @return array
|
||||
*/
|
||||
public function format($paymentIntent) {
|
||||
return $this->formatPaymentIntent($paymentIntent->id, $paymentIntent->amount, $paymentIntent->currency, $paymentIntent->status, $paymentIntent->metadata, $paymentIntent);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $id
|
||||
* @param $amount
|
||||
* @param $currency
|
||||
* @param $status
|
||||
* @param $metadata
|
||||
* @param $paymentIntent
|
||||
* @return array
|
||||
*/
|
||||
public function formatPaymentIntent($id, $amount, $currency, $status, $metadata, $paymentIntent): array {
|
||||
$formatter = app(CurrencyFormatterService::class);
|
||||
$iso_code = Setting::where('name', 'currency_iso_code')->value('value');
|
||||
$symbol = Setting::where('name', 'currency_symbol')->value('value');
|
||||
$position = Setting::where('name', 'currency_symbol_position')->value('value');
|
||||
|
||||
$currency = (object) [
|
||||
'iso_code' => $iso_code,
|
||||
'symbol' => $symbol,
|
||||
'symbol_position' => $position,
|
||||
];
|
||||
$displayAmount = $amount;
|
||||
// If it's NOT a zero-decimal currency, divide by 100 for the formatter
|
||||
// if (!in_array($iso_code, ['BIF', 'CLP', 'DJF', 'GNF', 'JPY', 'KMF', 'KRW', 'MGA', 'PYG', 'RWF', 'UGX', 'VND', 'VUV', 'XAF', 'XOF', 'XPF', 'ISK'])) {
|
||||
$displayAmount = $amount * 100;
|
||||
// }
|
||||
|
||||
|
||||
$formatted_final_price = $formatter->formatPrice($displayAmount ?? 0, $currency);
|
||||
return [
|
||||
'id' => $paymentIntent->id,
|
||||
'amount' => $paymentIntent->amount,
|
||||
'formatted_price' => $formatted_final_price,
|
||||
'currency' => $paymentIntent->currency,
|
||||
'metadata' => $paymentIntent->metadata,
|
||||
'status' => match ($paymentIntent->status) {
|
||||
"canceled" => "failed",
|
||||
"succeeded" => "succeed",
|
||||
"processing", "requires_action", "requires_capture", "requires_confirmation", "requires_payment_method" => "pending",
|
||||
},
|
||||
'payment_gateway_response' => $paymentIntent
|
||||
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $currency
|
||||
* @param $amount
|
||||
* @return float|int
|
||||
*/
|
||||
public function minimumAmountValidation($currency, $amount) {
|
||||
$minimumAmountMap = [
|
||||
'USD' => 0.50, 'EUR' => 0.50, 'INR' => 0.50, 'NZD' => 0.50, 'SGD' => 0.50,
|
||||
'BRL' => 0.50, 'CAD' => 0.50, 'AUD' => 0.50, 'CHF' => 0.50,
|
||||
'AED' => 2.00, 'PLN' => 2.00, 'RON' => 2.00,
|
||||
'BGN' => 1.00, 'CZK' => 15.00, 'DKK' => 2.50,
|
||||
'GBP' => 0.30, 'HKD' => 4.00, 'HUF' => 175.00,
|
||||
'JPY' => 50, 'MXN' => 10, 'THB' => 10, 'MYR' => 2,
|
||||
'NOK' => 3.00, 'SEK' => 3.00, 'XAF' => 100,
|
||||
'ISK' => 100 // ISK minimum is usually higher
|
||||
];
|
||||
|
||||
$zeroDecimalCurrencies = [
|
||||
'BIF', 'CLP', 'DJF', 'GNF', 'JPY', 'KMF', 'KRW', 'MGA', 'PYG',
|
||||
'RWF', 'UGX', 'VND', 'VUV', 'XAF', 'XOF', 'XPF', 'ISK' // Added ISK here
|
||||
];
|
||||
|
||||
$minimumAmount = $minimumAmountMap[$currency] ?? 0.50;
|
||||
|
||||
if (!in_array($currency, $zeroDecimalCurrencies)) {
|
||||
// Standard Currencies (USD, INR, etc.)
|
||||
$minimumAmount *= 100;
|
||||
$amount = (int)round($amount * 100);
|
||||
} else {
|
||||
// Zero-decimal Currencies
|
||||
if ($currency === 'ISK') {
|
||||
// Special Rule for ISK: Must be divisible by 100
|
||||
$amount = (int)round($amount / 100) * 100;
|
||||
if ($amount < 100) $amount = 100;
|
||||
} else {
|
||||
$amount = (int)$amount;
|
||||
}
|
||||
}
|
||||
|
||||
return max($amount, (int)$minimumAmount);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user