Skip to content

Commit ea844a4

Browse files
committed
wip
1 parent 2927f35 commit ea844a4

File tree

13 files changed

+647
-28
lines changed

13 files changed

+647
-28
lines changed

README.md

Lines changed: 108 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,14 @@
55
[![GitHub Code Style Action Status](https://img.shields.io/github/actions/workflow/status/wychoong/lunarphp-mpgs/fix-php-code-style-issues.yml?branch=main&label=code%20style&style=flat-square)](https://github.com/wychoong/lunarphp-mpgs/actions?query=workflow%3A"Fix+PHP+code+style+issues"+branch%3Amain)
66
[![Total Downloads](https://img.shields.io/packagist/dt/wychoong/lunarphp-mpgs.svg?style=flat-square)](https://packagist.org/packages/wychoong/lunarphp-mpgs)
77

8-
This is where your description should go. Limit it to a paragraph or two. Consider adding a small example.
8+
MPGS Hosted checkout integration for Lunar
99

10-
## Support us
11-
12-
[<img src="https://github-ads.s3.eu-central-1.amazonaws.com/lunarphp-mpgs.jpg?t=1" width="419px" />](https://spatie.be/github-ad-click/lunarphp-mpgs)
13-
14-
We invest a lot of resources into creating [best in class open source packages](https://spatie.be/open-source). You can support us by [buying one of our paid products](https://spatie.be/open-source/support-us).
15-
16-
We highly appreciate you sending us a postcard from your hometown, mentioning which of our package(s) you are using. You'll find our address on [our contact page](https://spatie.be/about-us). We publish all received postcards on [our virtual postcard wall](https://spatie.be/open-source/postcards).
10+
Supported action
11+
- purchase
12+
13+
Not supported (PR welcome)
14+
- refund
15+
- authorize/capture
1716

1817
## Installation
1918

@@ -23,37 +22,126 @@ You can install the package via composer:
2322
composer require wychoong/lunarphp-mpgs
2423
```
2524

26-
You can publish and run the migrations with:
25+
You can publish the config file with:
2726

2827
```bash
29-
php artisan vendor:publish --tag="lunarphp-mpgs-migrations"
30-
php artisan migrate
28+
php artisan vendor:publish --tag="lunarphp-mpgs-config"
3129
```
3230

33-
You can publish the config file with:
31+
###Enable the driver
32+
Set the driver in `config/lunar/payments.php`
33+
```php
34+
<?php
35+
36+
return [
37+
// ...
38+
'types' => [
39+
'card' => [
40+
'driver' => 'stripe',
41+
'authorized' => 'payment-received', # or any status key configured in lunar.orders.statuses
42+
],
43+
],
44+
];
45+
```
46+
47+
###Add your MPGS credentials
48+
Set your MPGS_ variable in `.env`
3449

3550
```bash
36-
php artisan vendor:publish --tag="lunarphp-mpgs-config"
51+
MPGS_MERCHANT_ID=
52+
MPGS_API_PASSWORD=
53+
MPGS_VERSION=
3754
```
3855

39-
This is the contents of the published config file:
56+
#Setup
57+
We use closure to return the data you want to pass to the api
4058

4159
```php
42-
return [
43-
];
60+
use \WyChoong\Mpgs\Facades\Mpgs;
61+
62+
// in service provider `boot` method
63+
Mpgs::initiateCheckoutUsing(function ($cart, $amount, $currency): array {
64+
if (!$order = $cart->order) {
65+
$order = $cart->createOrder();
66+
}
67+
68+
$reference = $order->reference . date('Ymdhis');
69+
70+
return [
71+
// refer to the api spec for Initiate Checkout params
72+
'order' => [
73+
'id' => $reference,
74+
'currency' => $currency,
75+
'amount' => $amount,
76+
'description' => "Payment for #" . $order->reference,
77+
'reference' => $reference,
78+
],
79+
'transaction' => [
80+
'reference' => $reference,
81+
],
82+
'interaction' => [
83+
'merchant' => [
84+
'name' => 'Lunar store',
85+
],
86+
'displayControl' => [
87+
'billingAddress' => 'HIDE',
88+
]
89+
]
90+
];
91+
});
4492
```
4593

46-
Optionally, you can publish the views using
94+
# Backend Usage
95+
96+
## Creating a PaymentIntent
97+
98+
```php
99+
use \WyChoong\Mpgs\Facades\Mpgs;
100+
101+
Mpgs::createIntent(\Lunar\Models\Cart $cart);
102+
```
103+
104+
This method will initiate a checkout session to be used by `checkout.js`
105+
Latest session and order.id are stored in cart's meta
106+
```php
107+
'meta' => [
108+
'payment_intent' => `session`,
109+
'order_id' => `order.id`,
110+
],
111+
```
112+
113+
# Storefront Usage
114+
115+
This package only provide basic blade components to interact with MPGS,, publish the views to fit your storefront design
47116

48117
```bash
49118
php artisan vendor:publish --tag="lunarphp-mpgs-views"
50119
```
51120

52-
## Usage
121+
## Set up the scripts and payment component
122+
123+
In the your checkout page
124+
```php
125+
@mpgsScripts
126+
127+
@if ($paymentType == 'card')
128+
<livewire:mpgs.payment :cart="$cart" />
129+
@endif
130+
```
131+
132+
The component will handle the success payment for you.
133+
To redirect or add handling after payment verified, set your route or listen to livewire event
53134

54135
```php
55-
$mpgsPaymentType = new WyChoong\MpgsPaymentType();
56-
echo $mpgsPaymentType->echoPhrase('Hello, WyChoong!');
136+
// config/lunar-mpgs.php
137+
'route' => [
138+
'payment-success' => null,
139+
'payment-failed' => null,
140+
]
141+
142+
// livewire events
143+
'mpgsPaymentSuccess'
144+
'mpgsPaymentFailed'
57145
```
58146

59147
## Testing

composer.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,12 @@
1717
],
1818
"require": {
1919
"php": "^8.1",
20-
"spatie/laravel-package-tools": "^1.14.0",
20+
"guzzlehttp/guzzle": "^7.5",
2121
"illuminate/contracts": "^9.0 | ^10.0",
22+
"illuminate/support": "^9.0 | ^10.0",
23+
"livewire/livewire": "^2.0",
2224
"lunarphp/lunar": "^0.3.0-alpha.2",
23-
"livewire/livewire": "^2.0"
25+
"spatie/laravel-package-tools": "^1.14.0"
2426
},
2527
"require-dev": {
2628
"laravel/pint": "^1.0",

config/lunar-mpgs.php

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,31 @@
11
<?php
22

33
// config for WyChoong/Mpgs
4-
return [];
4+
return [
5+
'merchant_id' => env('MPGS_MERCHANT_ID'),
6+
7+
'api_password' => env('MPGS_API_PASSWORD'),
8+
9+
'version' => env('MPGS_VERSION', '70'),
10+
11+
'gateway' => 'https://ap-gateway.mastercard.com/api/rest',
12+
13+
'checkout_js' => 'https://ap-gateway.mastercard.com/static/checkout/checkout.min.js',
14+
15+
'action' => [
16+
17+
'initiate_checkout' => '/session',
18+
19+
'retrieve_session' => '/session/{sessionId}',
20+
21+
'retrieve_order' => '/order/{orderId}',
22+
],
23+
24+
'route' => [
25+
26+
'payment-success' => null,
27+
28+
'payment-failed' => null,
29+
]
30+
31+
];
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<button x-on:click="checkout" style="padding: 5px 10px; border: 2px solid black;">Pay</button>
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<div id="mpgs-target"></div>
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<div
2+
wire:key="mpgs-form"
3+
x-data="{
4+
checkout(){
5+
$wire.checkout()
6+
.then(resp => {
7+
if(resp.session){
8+
Checkout.configure({
9+
session:{
10+
id: resp.session
11+
}
12+
});
13+
14+
Checkout.showEmbeddedPage('#mpgs-target');
15+
}
16+
})
17+
}
18+
}"
19+
@checkout='checkout'
20+
@mpgs-complete='$wire.checkoutSuccess()'
21+
>
22+
<x-lunar-mpgs::embed-container />
23+
<x-lunar-mpgs::checkout-button />
24+
</div>

src/Clients/Mpgs.php

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
<?php
2+
3+
namespace WyChoong\Mpgs\Clients;
4+
5+
use Closure;
6+
use Exception;
7+
use Illuminate\Http\Client\Response;
8+
use Illuminate\Support\Facades\Http;
9+
use Illuminate\Support\Facades\Log;
10+
11+
class Mpgs
12+
{
13+
protected static string $gateway;
14+
15+
protected static string $merchantId;
16+
17+
protected static string $apiPassword;
18+
19+
protected static string $version;
20+
21+
private static Closure | array $setupUsing = [];
22+
23+
public function __construct()
24+
{
25+
$this->setupClient();
26+
}
27+
28+
public static function setupClientUsing(Closure | array $setupUsing)
29+
{
30+
static::$setupUsing = $setupUsing;
31+
}
32+
33+
public static function setupClient()
34+
{
35+
$params = static::$setupUsing;
36+
37+
if ($params instanceof Closure) {
38+
$params = $params();
39+
}
40+
41+
foreach ($params as $key => $value) {
42+
if (in_array($key, ['gateway', 'merchantId', 'apiPassword', 'version']) && filled($value)) {
43+
static::$$key = $value;
44+
}
45+
}
46+
47+
static::$gateway ??= static::config('gateway');
48+
static::$merchantId ??= static::config('merchant_id');
49+
static::$apiPassword ??= static::config('api_password');
50+
static::$version ??= static::config('version');
51+
}
52+
53+
public static function config($key, $default = null): ?string
54+
{
55+
$value = config("lunar-mpgs.{$key}", $default);
56+
57+
if (!$value && str($key)->contains('action.')) {
58+
$action = str($key)->afterLast('action.')->toString();
59+
$value = match ($action) {
60+
'initiate_checkout' => '/session',
61+
'retrieve_session' => '/session/{sessionId}',
62+
'retrieve_order' => '/order/{orderId}',
63+
default => throw new Exception('Invalid action'),
64+
};
65+
}
66+
67+
return $value;
68+
}
69+
70+
protected function getUrl(string $action): string
71+
{
72+
return '{+gateway}/version/{version}/merchant/{merchantId}' . self::config("action.{$action}");
73+
}
74+
75+
protected function execute(string $method, string $action, array $data = [], array $urlParams = []): Response
76+
{
77+
$client = Http::withUrlParameters(array_merge([
78+
'gateway' => static::$gateway,
79+
'version' => static::$version,
80+
'merchantId' => static::$merchantId,
81+
], $urlParams));
82+
83+
return $client
84+
->withBasicAuth('merchant.' . static::$merchantId, static::$apiPassword)
85+
->{$method}($this->getUrl($action), $data);
86+
}
87+
88+
/**
89+
* Create a MPGS checkout session and return the result.
90+
*
91+
*/
92+
public static function initiateCheckout(array $data)
93+
{
94+
/** @var self $static */
95+
$static = app(static::class);
96+
97+
$data['apiOperation'] = 'INITIATE_CHECKOUT';
98+
$data['interaction']['operation'] = 'PURCHASE';
99+
100+
$response = $static->execute('post', 'initiate_checkout', data: $data);
101+
102+
return $response->object();
103+
}
104+
105+
/**
106+
* Retrieve MPGS checkout session details
107+
*
108+
*/
109+
public static function retrieveSession(string $sessionId)
110+
{
111+
/** @var self $static */
112+
$static = app(static::class);
113+
114+
$response = $static->execute('get', 'retrieve_session', urlParams: [
115+
'sessionId' => $sessionId,
116+
]);
117+
118+
return $response->object();
119+
}
120+
121+
/**
122+
* Retrieve the order details from MPGS.
123+
*
124+
*/
125+
public static function retrieveOrder($orderId)
126+
{
127+
/** @var self $static */
128+
$static = app(static::class);
129+
130+
$response = $static->execute('get', 'retrieve_order', urlParams: [
131+
'orderId' => $orderId,
132+
]);
133+
134+
return $response->object();
135+
}
136+
}

0 commit comments

Comments
 (0)