2 |
3 | # Selcom package for Laravel apps
4 |
5 | [](https://github.com/bryceandy/laravel-selcom/actions)
6 |
7 |
8 |
9 |
10 | This package enables Laravel developers to integrate their websites/APIs with all Selcom API services
11 |
12 | ## Installation
13 |
14 | Pre-installation requirements
15 |
16 | * Supports Laravel projects starting version 8.*
17 | * Minimum PHP version is 7.4
18 | * Your server must have the cURL PHP extension (ext-curl) installed
19 |
20 | Then proceed to install:
21 |
22 | ```
23 | composer require bryceandy/laravel-selcom
24 | ```
25 |
26 | ## Configuration
27 |
28 | To access Selcom's APIs, you will need to provide the package with access to your Selcom vendorID, API Key and Secret Key.
29 |
30 | After obtaining the three credentials from Selcom support, add their values in the `.env` variables:
31 |
32 | ```dotenv
33 | SELCOM_VENDOR_ID=123456
34 | SELCOM_API_KEY=yourApiKey
35 | SELCOM_API_SECRET=yourSecretKey
36 |
37 | SELCOM_IS_LIVE=false
38 | ```
39 |
40 | Note that when starting you will be provided with test credentials.
41 |
42 | When you change to live credentials don't forget to change `SELCOM_IS_LIVE` to `true`.
43 |
44 | We are going to update more configuration settings as we move along, but feel free to publish the config to fully customize it.
45 |
46 | ```
47 | php artisan vendor:publish --tag=selcom-config
48 | ```
49 |
50 | Run the migration command to create a table that stores Selcom payments:
51 |
52 | ```
53 | php artisan migrate
54 | ```
55 |
56 | ## Checkout API
57 |
58 | Checkout is the simplest Selcom API we can start processing payments with.
59 |
60 | ### Checkout payments using USSD
61 |
62 | This API automatically pulls your user's USSD payment menu directly after being called.
63 |
64 | **Note**: As of now, this is only applicable to AirtelMoney and TigoPesa customers.
65 |
66 | ```php
67 | use Bryceandy\Selcom\Facades\Selcom;
68 |
69 | Selcom::checkout([
70 | 'name' => "Buyer's full name",
71 | 'email' => "Buyer's email",
72 | 'phone' => "Buyer's msisdn, for example 255756334000",
73 | 'amount' => "Amount to be paid",
74 | 'transaction_id' => "Unique transaction id",
75 | 'no_redirection' => true,
76 | // Optional fields
77 | 'currency' => 'Default is TZS',
78 | 'items' => 'Number of items purchased, default is 1',
79 | 'payment_phone' => 'The number that will make the USSD transactions, if not specified it will use the phone value',
80 | ]);
81 | ```
82 |
83 | Other networks may use USSD checkout manually with tokens as shown with other checkout options below.
84 |
85 | ### Checkout to the payments page (without cards)
86 |
87 | The payment page contains payment options such as QR code, Masterpass, USSD wallet pull, mobile money payment with tokens.
88 |
89 | To redirect to this page, we will use the previous example, but **return** without the `no_redirection` option or assign it to `false`:
90 |
91 | ```php
92 | use Bryceandy\Selcom\Facades\Selcom;
93 |
94 | return Selcom::checkout([
95 | 'name' => "Buyer's full name",
96 | 'email' => "Buyer's email",
97 | 'phone' => "Buyer's msisdn, for example 255756334000",
98 | 'amount' => "Amount to be paid",
99 | 'transaction_id' => "Unique transaction id",
100 | ]);
101 | ```
102 |
103 | ### Checkout to the payments page (with cards)
104 |
105 | To use the cards on the payment page, return the following request:
106 |
107 | ```php
108 | use Bryceandy\Selcom\Facades\Selcom;
109 |
110 | return Selcom::cardCheckout([
111 | 'name' => "Buyer's full name",
112 | 'email' => "Buyer's email",
113 | 'phone' => "Buyer's msisdn, for example 255756334000",
114 | 'amount' => "Amount to be paid",
115 | 'transaction_id' => "Unique transaction id",
116 | 'address' => "Your buyer's address",
117 | 'postcode' => "Your buyer's postcode",
118 | // Optional fields
119 | 'user_id' => "Buyer's user ID in your system",
120 | 'buyer_uuid' => $buyerUuid, // Important if the user has to see their saved cards.
121 | // See the last checkout section on how to fetch a buyer's UUID
122 | 'country_code' => "Your buyer's ISO country code: Default is TZ",
123 | 'state' => "Your buyer's state: Default is Dar Es Salaam",
124 | 'city' => "Your buyer's city: Default is Dar Es Salaam",
125 | 'billing_phone' => "Your buyer's billing phone number: forexample 255756334000",
126 | 'currency' => 'Default is TZS',
127 | 'items' => 'Number of items purchased, default is 1',
128 | ]);
129 | ```
130 |
131 | Optionally, you may specify using the `.env` file the following:
132 |
133 | - The page where your users will be redirected once they complete a payment:
134 |
135 | ```dotenv
136 | SELCOM_REDIRECT_URL=https://mysite.com/selcom/redirect
137 | ```
138 |
139 | - The page where your users will be taken when they cancel the payment process:
140 |
141 | ```dotenv
142 | SELCOM_CANCEL_URL=https://mysite.com/selcom/cancel
143 | ```
144 |
145 | If you feel lazy, this package already ships with these pages for you. And if you want to customize them, run:
146 |
147 | ```
148 | php artisan vendor:publish --tag=selcom-views
149 | ```
150 |
151 |
152 |
153 |
154 |
155 |
156 | - Also, you can assign a prefix for the package. This will be applied to the routes and order IDs
157 |
158 | ```dotenv
159 | SELCOM_PREFIX=SHOP
160 | ```
161 |
162 | #### Customizing the payment page theme
163 |
164 | The configuration contains a `colors` field which specifies the theme of your payment page.
165 |
166 | To customize the colors, add the color values in the `.env` file:
167 |
168 | ```dotenv
169 | SELCOM_HEADER_COLOR="#FG345O"
170 | SELCOM_LINK_COLOR="#000000"
171 | SELCOM_BUTTON_COLOR="#E244FF"
172 | ```
173 |
174 | For JSON requests (API applications), this type of checkout to the payments page will return data with
175 | `payment_gateway_url` instead of redirecting to that page:
176 |
177 | ```json
178 | {
179 | "payment_gateway_url": "https://example.selcommobile-url.com"
180 | }
181 | ```
182 |
183 | ### Checkout payments with cards (without navigating to the payment page)
184 |
185 | To use a card without navigating to the payment page, you need to have already created a card for the paying user by navigating to the payment page.
186 |
187 | This is very useful for recurring or on-demand card payments. The data is the same as the previous card checkout, except we are adding `no_redirection`, `user_id` & `buyer_uuid`:
188 |
189 | ```php
190 | use Bryceandy\Selcom\Facades\Selcom;
191 |
192 | Selcom::cardCheckout([
193 | 'name' => "Buyer's full name",
194 | 'email' => "Buyer's email",
195 | 'phone' => "Buyer's msisdn, for example 255756334000",
196 | 'amount' => "Amount to be paid",
197 | 'transaction_id' => "Unique transaction id",
198 | 'no_redirection' => true,
199 | 'user_id' => "Buyer's user ID in your system",
200 | 'buyer_uuid' => $buyerUuid, // See instructions below on how to obtain this value
201 | 'address' => "Your buyer's address",
202 | 'postcode' => "Your buyer's postcode",
203 | // Optional fields
204 | 'country_code' => "Your buyer's ISO country code: Default is TZ",
205 | 'state' => "Your buyer's state: Default is Dar Es Salaam",
206 | 'city' => "Your buyer's city: Default is Dar Es Salaam",
207 | 'billing_phone' => "Your buyer's billing phone number: forexample 255756334000",
208 | 'currency' => 'Default is TZS',
209 | 'items' => 'Number of items purchased, default is 1',
210 | ]);
211 | ```
212 |
213 | This method will fetch 3 saved cards of the user and try all of them until a payment is successful or all fail.
214 |
215 | #### Obtaining the buyer's UUID
216 |
217 | If this user has visited the payment page before to make a payment, then their uuid is already in the database.
218 |
219 | ```php
220 | use Illuminate\Support\Facades\DB;
221 |
222 | $buyerUuid = DB::table('selcom_payments')
223 | ->where([
224 | ['user_id', '=' auth()->id()],
225 | ['gateway_buyer_uuid', '<>', null],
226 | ])
227 | ->value('gateway_buyer_uuid');
228 | ```
229 |
230 | ### Listing a user's stored cards
231 |
232 | To fetch the user's stored cards could be useful to know if a user has cards, or if there is a need to delete.
233 |
234 | You will require a user's ID and `buyer_uuid`:
235 |
236 | ```php
237 | use Bryceandy\Selcom\Facades\Selcom;
238 |
239 | Selcom::fetchCards($userId, $gatewayBuyerUuid);
240 | ```
241 |
242 | ### Deleting a user's stored card
243 |
244 | To delete a user's stored card you need a `buyer_uuid` and card ID obtained from `fetchCards` request above.
245 |
246 | ```php
247 | use Bryceandy\Selcom\Facades\Selcom;
248 |
249 | Selcom::deleteCard($cardId, $gatewayBuyerUuid);
250 | ```
251 |
252 | ### Checkout webhook/callback
253 |
254 | The package comes with an implementation of the payment webhook.
255 |
256 | When Selcom sends the payment status to your site, the data in the `selcom_payments` table will be updated and an event `Bryceandy\Selcom\Events\CheckoutWebhookReceived` will be dispatched.
257 |
258 | You can create a listener for the event:
259 |
260 | ```php
261 | class EventServiceProvider extends ServiceProvider
262 | {
263 | protected $listen = [
264 | \Bryceandy\Selcom\Events\CheckoutWebhookReceived::class => [
265 | \App\Listeners\ProcessWebhook::class,
266 | ],
267 | ];
268 | }
269 | ```
270 |
271 | Then in your listener `App\Listeners\ProcessWebhook`, you can do anything with the order ID:
272 |
273 | ```php
274 | orderId;
293 |
294 | // Fetch updated record in the database, and do what you need with it
295 | $status = DB::table('selcom_payments')
296 | ->where('order_id', $orderId)
297 | ->value('payment_status');
298 |
299 | if ($status === 'PENDING') {
300 | Selcom::orderStatus($orderId); // Or dispatch a job minutes later to query order status
301 | }
302 | }
303 | }
304 | ```
305 |
306 | ### Check order status
307 |
308 | To query order statuses to Selcom, simply run:
309 |
310 | ```php
311 | use Bryceandy\Selcom\Facades\Selcom;
312 | use Illuminate\Support\Facades\DB;
313 |
314 | $order = Selcom::orderStatus($orderId);
315 | ```
316 |
317 | Once you have obtained the order data, you can use it as you wish. The example below updates the payment in the database:
318 |
319 | ```php
320 | DB::table('selcom_payments')->where('order_id', $orderId)
321 | ->update(array_merge(
322 | 'payment_status' => $order['payment_status'],
323 | ($order['payment_status'] === 'COMPLETED'
324 | ? [
325 | 'selcom_transaction_id' => $order['transid'],
326 | 'channel' => $order['channel'],
327 | 'reference' => $order['reference'],
328 | 'msisdn' => $order['msisdn'],
329 | ]
330 | : []
331 | )
332 | ));
333 | ```
334 |
335 | ### List orders
336 |
337 | To list all orders made to Selcom, simply indicate `from_date` and `to_date`:
338 |
339 | ```php
340 | use Bryceandy\Selcom\Facades\Selcom;
341 |
342 | $fromDate = '2021-02-16';
343 | $toDate = '2021-12-25';
344 |
345 | Selcom::listOrders($fromDate, $toDate);
346 | ```
347 |
348 | ### Cancel order
349 |
350 | To cancel a Selcom order, simply run:
351 |
352 | ```php
353 | use Bryceandy\Selcom\Facades\Selcom;
354 |
355 | Selcom::cancelOrder($orderId);
356 | ```
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "bryceandy/laravel-selcom",
3 | "description": "Laravel package that integrates with Selcom APIs (Utility Payments, Wallet Cashin, Agent Cashout, C2B, Qwiksend, VCN, Checkout & International Money Transfer",
4 | "license": "MIT",
5 | "authors": [
6 | {
7 | "name": "bryceandy",
8 | "email": "hello@bryceandy.com"
9 | }
10 | ],
11 | "minimum-stability": "stable",
12 | "prefer-stable": true,
13 | "require": {
14 | "php": "^7.4|^8.0",
15 | "ext-curl": "*",
16 | "guzzlehttp/guzzle": "^7.3"
17 | },
18 | "require-dev": {
19 | "orchestra/testbench": "^6.19",
20 | "ext-json": "*"
21 | },
22 | "autoload": {
23 | "psr-4": {
24 | "Bryceandy\\Selcom\\": "src/"
25 | }
26 | },
27 | "autoload-dev": {
28 | "psr-4": {
29 | "Bryceandy\\Selcom\\Tests\\": "tests/"
30 | }
31 | },
32 | "extra": {
33 | "laravel": {
34 | "providers": [
35 | "Bryceandy\\Selcom\\SelcomBaseServiceProvider"
36 | ]
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/config/selcom.php:
--------------------------------------------------------------------------------
1 | env('SELCOM_VENDOR_ID'),
13 |
14 | /*
15 | |--------------------------------------------------------------------------
16 | | API Key
17 | |--------------------------------------------------------------------------
18 | |
19 | | Merchant API key
20 | */
21 | 'key' => env('SELCOM_API_KEY'),
22 |
23 | /*
24 | |--------------------------------------------------------------------------
25 | | API Secret
26 | |--------------------------------------------------------------------------
27 | |
28 | | Merchant API secret
29 | */
30 | 'secret' => env('SELCOM_API_SECRET'),
31 |
32 | /*
33 | |--------------------------------------------------------------------------
34 | | Selcom live status
35 | |--------------------------------------------------------------------------
36 | |
37 | | This determines if you are using Selcom in live mode.
38 | | The credentials would be different in every stage.
39 | |
40 | | SELCOM_API_KEY and SELCOM_API_SECRET should be
41 | | different when changing between live & test.
42 | */
43 | 'live' => env('SELCOM_IS_LIVE', false),
44 |
45 | /*
46 | |--------------------------------------------------------------------------
47 | | Selcom prefix
48 | |--------------------------------------------------------------------------
49 | |
50 | | This prefix will be used for routes and on Selcom order IDs.
51 | */
52 | 'prefix' => env('SELCOM_PREFIX', 'selcom'),
53 |
54 | /*
55 | |--------------------------------------------------------------------------
56 | | Redirect URL
57 | |--------------------------------------------------------------------------
58 | |
59 | | The URL where your users will be taken to after a payment is complete.
60 | | Eg: https://www.myshop.co.tz/checkout/redirect
61 | */
62 | 'redirect_url' => env('SELCOM_REDIRECT_URL'),
63 |
64 | /*
65 | |--------------------------------------------------------------------------
66 | | Cancel URL
67 | |--------------------------------------------------------------------------
68 | |
69 | | The URL where your users will be taken to when they cancel the payment.
70 | | Eg: https://www.myshop.co.tz/checkout/cancel
71 | */
72 | 'cancel_url' => env('SELCOM_CANCEL_URL'),
73 |
74 | /*
75 | |--------------------------------------------------------------------------
76 | | Payment Gateway Colors
77 | |--------------------------------------------------------------------------
78 | |
79 | | Colors for your payment gateway page.
80 | */
81 | 'colors' => [
82 | 'header' => env('SELCOM_HEADER_COLOR', '#FF0012'),
83 | 'link' => env('SELCOM_LINK_COLOR', '#FF0012'),
84 | 'button' => env('SELCOM_BUTTON_COLOR', '#FF0012'),
85 | ],
86 |
87 | /*
88 | |--------------------------------------------------------------------------
89 | | Payment Expiry
90 | |--------------------------------------------------------------------------
91 | |
92 | | Time in minutes before the payment gateway page expires.
93 | */
94 | 'expiry' => env('SELCOM_PAYMENT_EXPIRY', 60)
95 | ];
96 |
--------------------------------------------------------------------------------
/database/migrations/2021_13_09_000000_create_selcom_payments_table.php:
--------------------------------------------------------------------------------
1 | id();
18 | $table->integer('amount');
19 | $table->string('order_id')->unique();
20 | $table->string('transid')->unique();
21 | $table->string('selcom_transaction_id')->nullable();
22 | $table->string('user_id')->nullable();
23 | $table->string('gateway_buyer_uuid')->nullable();
24 | $table->string('payment_status')->nullable();
25 | $table->string('reference')->nullable();
26 | $table->string('msisdn')->nullable();
27 | $table->string('channel')->nullable();
28 | $table->timestamps();
29 | });
30 | }
31 |
32 | /**
33 | * Reverse the migrations.
34 | *
35 | * @return void
36 | */
37 | public function down()
38 | {
39 | Schema::dropIfExists('selcom_payments');
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/phpunit.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 | ./tests/Feature
11 |
12 |
13 |
14 |
15 | ./src
16 |
17 |
18 |
--------------------------------------------------------------------------------
/resources/views/cancel.blade.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
7 |
8 |
9 | Selcom | Payment Cancelled
10 |
11 |
12 |
13 |
14 |
15 | {{ config('app.name') }}
16 |
17 |
18 |
19 | Your payment is incomplete. Would you like to go back and try again or return home?
20 |