Skip to content

Commit c2354f2

Browse files
authored
Merge pull request #6 from MusahMusah/flutterwave-integration
Add Support for Flutterwave integration
2 parents 47eb460 + ecda9f3 commit c2354f2

17 files changed

+1353
-4
lines changed

config/multipayment-gateways.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,13 @@
1919
],
2020
],
2121

22+
'flutterwave' => [
23+
'base_uri' => env('FLUTTERWAVE_BASE_URI'),
24+
'secret' => env('FLUTTERWAVE_SECRET'),
25+
'currency' => env('FLUTTERWAVE_CURRENCY'),
26+
'encryption_key' => env('FLUTTERWAVE_ENCRYPTION_KEY'),
27+
],
28+
2229
'webhooks' => [
2330
[
2431
/*

src/Abstracts/BaseGateWay.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,12 @@ abstract class BaseGateWay implements GatewayContract
1414
/**
1515
* The base uri to consume the payment gateway's service
1616
*/
17-
protected ?string $baseUri;
17+
protected $baseUri;
1818

1919
/**
2020
* The secret to consume the payment gateway's service
2121
*/
22-
protected ?string $secret;
22+
protected $secret;
2323

2424
/**
2525
* The redirect url to consume the payment gateway's service
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
<?php
2+
3+
namespace MusahMusah\LaravelMultipaymentGateways\Constants;
4+
5+
class FlutterwaveConstant
6+
{
7+
const CHARGE_ENDPOINT = '/charges/';
8+
9+
const BANK_ENDPOINT = '/banks/';
10+
11+
const OTP_ENDPOINT = '/otps/';
12+
13+
const PAYMENT_PLAN_ENDPOINT = '/payment-plans/';
14+
15+
const SETTLEMENT_ENDPOINT = '/settlements/';
16+
17+
const SUBSCRIPTION_ENDPOINT = '/subscriptions/';
18+
19+
const TRANSACTION_ENDPOINT = '/transactions/';
20+
21+
const REFUND_ENDPOINT = '/refunds/';
22+
23+
const BENEFICIARY_ENDPOINT = '/beneficiaries/';
24+
25+
const TRANSFER_ENDPOINT = '/transfers/';
26+
27+
const BULK_TRANFER_ENDPOINT = '/bulk-transfers/';
28+
29+
const VALIDATE_CHARGE_ENDPOINT = '/validate-charge';
30+
31+
const CARD_PAYMENT_CHARGE_TYPE = 'card';
32+
33+
const BANK_TRANSFER_CHARGE_TYPE = 'bank_transfer';
34+
35+
const NG_ACCOUNT_DEBIT_TYPE = 'debit_ng_account';
36+
37+
const UK_ACCOUNT_DEBIT_TYPE = 'debit_uk_account';
38+
39+
const ACH_PAYMENT_CHARGE_TYPE = 'ach_payment';
40+
41+
const APPLE_PAY_CHARGE_TYPE = 'applepay';
42+
43+
const GOOGLE_PAY_CHARGE_TYPE = 'googlepay';
44+
45+
const FAWRY_PAY_CHARGE_TYPE = 'fawry_pay';
46+
47+
const PAYPAL_CHARGE_TYPE = 'paypal';
48+
49+
const MPESA_PAY_CHARGE_TYPE = 'mpesa';
50+
51+
const GHANA_MOBILE_MONEY_CHARGE_TYPE = 'mobile_money_ghana';
52+
53+
const UGANDA_MOBILE_MONEY_CHARGE_TYPE = 'mobile_money_uganda';
54+
55+
const FRANCOPHONE_MOBILE_MONEY_CHARGE_TYPE = 'mobile_money_franco';
56+
57+
const RWANDA_MOBILE_MONEY_CHARGE_TYPE = 'mobile_money_rwanda';
58+
59+
const ZAMBIA_MOBILE_MONEY_CHARGE_TYPE = 'mobile_money_zambia';
60+
61+
const USSD_CHARGE_TYPE = 'ussd';
62+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php
2+
3+
namespace MusahMusah\LaravelMultipaymentGateways\Contracts;
4+
5+
interface FlutterwaveContract
6+
{
7+
/**
8+
* Resolve the authorization URL / Endpoint
9+
*/
10+
public function resolveAuthorization(&$queryParams, &$formParams, &$headers): void;
11+
12+
/**
13+
* Set the access token for the request
14+
*/
15+
public function resolveAccessToken(): string;
16+
17+
/**
18+
* Decode the response
19+
*/
20+
public function decodeResponse(): mixed;
21+
22+
/**
23+
* Get the response
24+
*/
25+
public function getResponse(): mixed;
26+
27+
/**
28+
* Get the data from the response
29+
*/
30+
public function getData(): mixed;
31+
}

src/Facades/Flutterwave.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?php
2+
3+
namespace MusahMusah\LaravelMultipaymentGateways\Facades;
4+
5+
use Illuminate\Support\Facades\Facade;
6+
use MusahMusah\LaravelMultipaymentGateways\Contracts\FlutterwaveContract;
7+
8+
class Flutterwave extends Facade
9+
{
10+
protected static function getFacadeAccessor()
11+
{
12+
return FlutterwaveContract::class;
13+
}
14+
}
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace MusahMusah\LaravelMultipaymentGateways\Gateways;
6+
7+
use MusahMusah\LaravelMultipaymentGateways\Abstracts\BaseGateWay;
8+
use MusahMusah\LaravelMultipaymentGateways\Contracts\FlutterwaveContract;
9+
use MusahMusah\LaravelMultipaymentGateways\Exceptions\InvalidConfigurationException;
10+
use MusahMusah\LaravelMultipaymentGateways\Traits\ConsumesExternalServices;
11+
use MusahMusah\LaravelMultipaymentGateways\Traits\Flutterwave\BankTrait;
12+
use MusahMusah\LaravelMultipaymentGateways\Traits\Flutterwave\ChargeTrait;
13+
use MusahMusah\LaravelMultipaymentGateways\Traits\Flutterwave\OtpTrait;
14+
use MusahMusah\LaravelMultipaymentGateways\Traits\Flutterwave\PaymentPlanTrait;
15+
use MusahMusah\LaravelMultipaymentGateways\Traits\Flutterwave\SettlementTrait;
16+
use MusahMusah\LaravelMultipaymentGateways\Traits\Flutterwave\SubscriptionTrait;
17+
use MusahMusah\LaravelMultipaymentGateways\Traits\Flutterwave\TransactionTrait;
18+
use MusahMusah\LaravelMultipaymentGateways\Traits\Flutterwave\TransferBeneficiaryTrait;
19+
use MusahMusah\LaravelMultipaymentGateways\Traits\Flutterwave\TransferTrait;
20+
21+
class FlutterwaveService extends BaseGateWay implements FlutterwaveContract
22+
{
23+
use ConsumesExternalServices,
24+
BankTrait,
25+
SettlementTrait,
26+
SubscriptionTrait,
27+
PaymentPlanTrait,
28+
TransferBeneficiaryTrait,
29+
TransferTrait,
30+
OtpTrait,
31+
ChargeTrait,
32+
TransactionTrait;
33+
34+
/**
35+
* The redirect url to consume the Flutterwave's service
36+
*/
37+
protected string $redirectUrl;
38+
39+
/**
40+
* The payload to initiate the transaction
41+
*/
42+
protected array $payload;
43+
44+
/**
45+
* The encryption key to encrypt payload for direct card charge
46+
*/
47+
protected string $encryptionKey;
48+
49+
public function __construct()
50+
{
51+
$this->setPaymentGateway();
52+
$this->setBaseUri();
53+
$this->setSecret();
54+
$this->setEncryptionKey();
55+
}
56+
57+
/**
58+
* Set the payment gateway for the class
59+
*/
60+
public function setPaymentGateway(): void
61+
{
62+
$this->paymentGateway = 'flutterwave';
63+
}
64+
65+
/**
66+
* Set the encryption key for the class
67+
*/
68+
public function setEncryptionKey(): void
69+
{
70+
$encryptionKey = config('multipayment-gateways.flutterwave.encryption_key');
71+
72+
if (! $encryptionKey) {
73+
return;
74+
}
75+
76+
$this->encryptionKey = $encryptionKey;
77+
}
78+
79+
/**
80+
* Set the base URI for the API request
81+
*
82+
* @throws InvalidConfigurationException
83+
*/
84+
public function setBaseUri(): void
85+
{
86+
$baseUri = config('multipayment-gateways.flutterwave.base_uri');
87+
88+
if (! $baseUri) {
89+
throw new InvalidConfigurationException("The Base URI for `{$this->paymentGateway}` is missing. Please ensure that the `base_uri` config key for `{$this->paymentGateway}` is set correctly.");
90+
}
91+
92+
$this->baseUri = $baseUri;
93+
}
94+
95+
/**
96+
* Set the secret key for the API request
97+
*
98+
* @throws InvalidConfigurationException
99+
*/
100+
public function setSecret(): void
101+
{
102+
$secret = config('multipayment-gateways.flutterwave.secret');
103+
104+
if (! $secret) {
105+
throw new InvalidConfigurationException("The secret key for `{$this->paymentGateway}` is missing. Please ensure that the `secret` config key for `{$this->paymentGateway}` is set correctly.");
106+
}
107+
108+
$this->secret = $secret;
109+
}
110+
111+
/**
112+
* Resolve the authorization URL / Endpoint
113+
*/
114+
public function resolveAuthorization(&$queryParams, &$formParams, &$headers): void
115+
{
116+
$headers['Authorization'] = $this->resolveAccessToken();
117+
}
118+
119+
/**
120+
* Set the access token for the request
121+
*/
122+
public function resolveAccessToken(): string
123+
{
124+
return "Bearer {$this->secret}";
125+
}
126+
127+
/**
128+
* Decode the response
129+
*/
130+
public function decodeResponse(): array
131+
{
132+
return json_decode($this->response, true);
133+
}
134+
135+
/**
136+
* Get the response
137+
*/
138+
public function getResponse(): array
139+
{
140+
return $this->response;
141+
}
142+
143+
/**
144+
* Get the data from the response
145+
*/
146+
public function getData(): array
147+
{
148+
return $this->getResponse()['data'];
149+
}
150+
}

src/LaravelMultipaymentGatewaysServiceProvider.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@
44

55
use Illuminate\Support\Facades\Route;
66
use Illuminate\Support\Str;
7+
use MusahMusah\LaravelMultipaymentGateways\Contracts\FlutterwaveContract;
78
use MusahMusah\LaravelMultipaymentGateways\Contracts\PaystackContract;
89
use MusahMusah\LaravelMultipaymentGateways\Contracts\StripeContract;
910
use MusahMusah\LaravelMultipaymentGateways\Exceptions\InvalidPaymentWebhookConfig;
11+
use MusahMusah\LaravelMultipaymentGateways\Gateways\FlutterwaveService;
1012
use MusahMusah\LaravelMultipaymentGateways\Gateways\PaystackService;
1113
use MusahMusah\LaravelMultipaymentGateways\Gateways\StripeService;
1214
use MusahMusah\LaravelMultipaymentGateways\Http\Controllers\PaymentWebhookController;
@@ -29,6 +31,7 @@ public function packageRegistered()
2931
{
3032
$this->app->bind(PaystackContract::class, PaystackService::class);
3133
$this->app->bind(StripeContract::class, StripeService::class);
34+
$this->app->bind(FlutterwaveContract::class, FlutterwaveService::class);
3235

3336
$this->registerWebHookConfig();
3437
}
@@ -40,6 +43,7 @@ public function provides(): array
4043
StripeContract::class,
4144
PaymentWebhookConfigRepository::class,
4245
PaymentWebhookConfig::class,
46+
FlutterwaveContract::class,
4347
];
4448
}
4549

src/Traits/ConsumesExternalServices.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ trait ConsumesExternalServices
1919
*
2020
* @throws GuzzleException|HttpMethodFoundException
2121
*/
22-
public function makeRequest(string $method, string $requestUrl, array $formParams = [], bool $isJsonRequest = false, array $queryParams = [], array $headers = [], bool $skipResolve = false): mixed
22+
public function makeRequest(string $method, string $requestUrl, array|string $formParams = [], bool $isJsonRequest = false, array $queryParams = [], array $headers = [], bool $skipResolve = false): mixed
2323
{
2424
$this->validateRequest($method);
2525

@@ -55,7 +55,7 @@ public function makeRequest(string $method, string $requestUrl, array $formParam
5555
*/
5656
private function validateRequest(string $method): void
5757
{
58-
if (! in_array($method, ['GET', 'POST', 'DELETE'])) {
58+
if (! in_array($method, ['GET', 'POST', 'PUT', 'DELETE'])) {
5959
throw new HttpMethodFoundException('Method not found');
6060
}
6161
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<?php
2+
3+
namespace MusahMusah\LaravelMultipaymentGateways\Traits\Flutterwave;
4+
5+
use GuzzleHttp\Exception\GuzzleException;
6+
use MusahMusah\LaravelMultipaymentGateways\Constants\FlutterwaveConstant;
7+
use MusahMusah\LaravelMultipaymentGateways\Exceptions\HttpMethodFoundException;
8+
9+
trait BankTrait
10+
{
11+
/**
12+
* Get list of banks for a given country by shortcode.
13+
*/
14+
public function getBanks(string $countryCode): array
15+
{
16+
$banks = $this->makeRequest(
17+
method: 'GET',
18+
requestUrl: FlutterwaveConstant::BANK_ENDPOINT.$countryCode,
19+
isJsonRequest: true
20+
);
21+
22+
// sort banks by name
23+
array_multisort(array_column($banks['data'], 'name'), SORT_ASC, $banks['data']);
24+
25+
return $banks;
26+
}
27+
28+
/**
29+
* Get all branches of a bank
30+
*
31+
* @param int $bankId The ID of the bank for which to retrieve branches
32+
*
33+
* @throws GuzzleException|HttpMethodFoundException
34+
*/
35+
public function getBankBranches(int $bankId): array
36+
{
37+
return $this->makeRequest(
38+
method: 'GET',
39+
requestUrl: FlutterwaveConstant::BANK_ENDPOINT.$bankId.'/branches',
40+
isJsonRequest: true
41+
);
42+
}
43+
}

0 commit comments

Comments
 (0)