@mandarvl/vanilla-pay-international 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2026 Manda Ravalison
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,110 @@
1
+ # @mandarvl/vanilla-pay-international
2
+
3
+ A Node.js SDK for **Vanilla Pay International API**, supporting payments via mobile money, international cards, and more.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @mandarvl/vanilla-pay-international
9
+ ```
10
+
11
+ ---
12
+
13
+ ## Usage
14
+
15
+ ### 1. Setup environment variables
16
+
17
+ After creating and generating your keys from Vanilla Pay International, in your environment variables, add these :
18
+
19
+ ```env
20
+ CLIENT_ID=your_client_id
21
+ CLIENT_SECRET=your_client_secret
22
+ ENV=PROD # or PREPROD
23
+ ```
24
+
25
+ ---
26
+
27
+ ### 2. Initialize client and make a payment
28
+
29
+ ```ts
30
+ import dotenv from 'dotenv';
31
+ import { VanillaPayInternational, Environment } from '@mandarvl/vanilla-pay-international';
32
+
33
+ dotenv.config();
34
+
35
+ async function main() {
36
+ const clientId = process.env.CLIENT_ID;
37
+ const clientSecret = process.env.CLIENT_SECRET;
38
+ const env = process.env.ENV as Environment;
39
+
40
+ if (!clientId || !clientSecret || !env) {
41
+ throw new Error('CLIENT_ID, CLIENT_SECRET, and ENV are required');
42
+ }
43
+
44
+ const vanillaPay = new VanillaPayInternational(env);
45
+
46
+ try {
47
+ const auth = await vanillaPay.auth.generateToken(clientId, clientSecret);
48
+ vanillaPay.setAccessToken(auth.Token);
49
+
50
+ const payment = await vanillaPay.payment.init({
51
+ amount: 10000,
52
+ cart: '123',
53
+ currency: 'MGA',
54
+ reference: '123',
55
+ paymentMethod: 'mobile_money',
56
+ callbackUrl: 'https://your-callback-url.com',
57
+ redirectUrl: 'https://your-redirect-url.com'
58
+ });
59
+
60
+ console.log(payment);
61
+
62
+ const paymentDetails = await vanillaPay.payment.get(payment.id);
63
+ console.log(paymentDetails);
64
+ } catch (err) {
65
+ console.error(err);
66
+ }
67
+ }
68
+
69
+ main();
70
+ ```
71
+
72
+ ---
73
+
74
+ ### 3. Handle Payment Callbacks
75
+
76
+ Vanilla Pay International posts to your `callbackUrl` with headers:
77
+
78
+ * `VPI-Signature`
79
+ * `Content-Type: application/x-www-form-urlencoded`
80
+
81
+ Validate the payload using:
82
+
83
+ ```ts
84
+ import { Utils } from '@mandarvl/vanilla-pay-international';
85
+
86
+ const isValid = Utils.formatAndValidatePayload(vpiSignature, body, keySecret);
87
+
88
+ if (isValid) {
89
+ // process payment
90
+ } else {
91
+ // invalid signature
92
+ }
93
+ ```
94
+
95
+ * `vpiSignature`: value from headers
96
+ * `body`: payload object
97
+ * `keySecret`: generated from your account
98
+
99
+ ---
100
+
101
+ ### 4. Supported Payment Methods
102
+
103
+ * `mobile_money` (Mvola, Orange Money, Airtel Money, etc.)
104
+ * `international` (Visa, Mastercard, PayPal)
105
+
106
+ ---
107
+
108
+ ### 5. License
109
+
110
+ MIT © Manda Ravalison
package/dist/auth.d.ts ADDED
@@ -0,0 +1,6 @@
1
+ import { Service } from "./shared/service";
2
+ import { AuthResponse } from "./shared/type";
3
+ declare class AuthService extends Service {
4
+ generateToken(clientId: string, clientSecret: string): Promise<AuthResponse>;
5
+ }
6
+ export default AuthService;
package/dist/auth.js ADDED
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ const service_1 = require("./shared/service");
13
+ class AuthService extends service_1.Service {
14
+ generateToken(clientId, clientSecret) {
15
+ var _a;
16
+ return __awaiter(this, void 0, void 0, function* () {
17
+ try {
18
+ const { data } = yield this.httpClient.get("/webpayment/token", {
19
+ headers: {
20
+ 'Client-Id': clientId,
21
+ 'Client-Secret': clientSecret
22
+ }
23
+ });
24
+ return data.Data;
25
+ }
26
+ catch (error) {
27
+ throw ((_a = error === null || error === void 0 ? void 0 : error.response) === null || _a === void 0 ? void 0 : _a.data) || (error === null || error === void 0 ? void 0 : error.response) || error;
28
+ }
29
+ });
30
+ }
31
+ }
32
+ exports.default = AuthService;
@@ -0,0 +1,5 @@
1
+ import VanillaPayInternational from "./vanilla-pay-international";
2
+ import PaymentService from "./payment";
3
+ import { Util } from "./shared/util";
4
+ import { Environment, PaymentRequest, PaymentResponse, AuthResponse, PaymentStatus, PaymentDetails, PaymentMethod } from "./shared/type";
5
+ export { VanillaPayInternational, PaymentService, Util, Environment, PaymentMethod, PaymentRequest, PaymentResponse, PaymentStatus, PaymentDetails, AuthResponse };
package/dist/index.js ADDED
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.PaymentStatus = exports.Util = exports.PaymentService = exports.VanillaPayInternational = void 0;
7
+ const vanilla_pay_international_1 = __importDefault(require("./vanilla-pay-international"));
8
+ exports.VanillaPayInternational = vanilla_pay_international_1.default;
9
+ const payment_1 = __importDefault(require("./payment"));
10
+ exports.PaymentService = payment_1.default;
11
+ const util_1 = require("./shared/util");
12
+ Object.defineProperty(exports, "Util", { enumerable: true, get: function () { return util_1.Util; } });
13
+ const type_1 = require("./shared/type");
14
+ Object.defineProperty(exports, "PaymentStatus", { enumerable: true, get: function () { return type_1.PaymentStatus; } });
@@ -0,0 +1,7 @@
1
+ import { Service } from "./shared/service";
2
+ import { PaymentDetails, PaymentRequest, PaymentResponse } from "./shared/type";
3
+ declare class PaymentService extends Service {
4
+ init(payData: PaymentRequest): Promise<PaymentResponse>;
5
+ get(id: string): Promise<PaymentDetails>;
6
+ }
7
+ export default PaymentService;
@@ -0,0 +1,53 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ const const_1 = require("./shared/const");
13
+ const service_1 = require("./shared/service");
14
+ const util_1 = require("./shared/util");
15
+ class PaymentService extends service_1.Service {
16
+ init(payData) {
17
+ var _a;
18
+ return __awaiter(this, void 0, void 0, function* () {
19
+ try {
20
+ const payload = {
21
+ montant: payData.amount,
22
+ reference: payData.reference,
23
+ panier: payData.cart,
24
+ notif_url: payData.callbackUrl,
25
+ redirect_url: payData.redirectUrl,
26
+ devise: payData.currency,
27
+ mode_paiement: payData.paymentMethod
28
+ };
29
+ const { data } = yield this.httpClient.post(`/api/webpayment/${const_1.CURRENT_API_VERSION}/initiate`, payload);
30
+ return {
31
+ id: data.Data.url.split('?id=')[1],
32
+ url: data.Data.url
33
+ };
34
+ }
35
+ catch (error) {
36
+ throw ((_a = error === null || error === void 0 ? void 0 : error.response) === null || _a === void 0 ? void 0 : _a.data) || (error === null || error === void 0 ? void 0 : error.response) || error;
37
+ }
38
+ });
39
+ }
40
+ get(id) {
41
+ var _a;
42
+ return __awaiter(this, void 0, void 0, function* () {
43
+ try {
44
+ const { data } = yield this.httpClient.get(`/api/webpayment/${const_1.CURRENT_API_VERSION}/status/${id}`);
45
+ return Object.assign(Object.assign({}, util_1.Util.formatPaymentData(data.Data)), { id });
46
+ }
47
+ catch (error) {
48
+ throw ((_a = error === null || error === void 0 ? void 0 : error.response) === null || _a === void 0 ? void 0 : _a.data) || (error === null || error === void 0 ? void 0 : error.response) || error;
49
+ }
50
+ });
51
+ }
52
+ }
53
+ exports.default = PaymentService;
@@ -0,0 +1,4 @@
1
+ export declare const PREPROD_URL = "https://preprod.vanilla-pay.net/";
2
+ export declare const PROD_URL = "https://bo.vanilla-pay.net/";
3
+ export declare const CURRENT_API_VERSION = "v2";
4
+ export declare const CURRENT_VPI_VERSION = "2023-01-12";
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CURRENT_VPI_VERSION = exports.CURRENT_API_VERSION = exports.PROD_URL = exports.PREPROD_URL = void 0;
4
+ exports.PREPROD_URL = 'https://preprod.vanilla-pay.net/';
5
+ exports.PROD_URL = 'https://bo.vanilla-pay.net/';
6
+ exports.CURRENT_API_VERSION = 'v2';
7
+ exports.CURRENT_VPI_VERSION = '2023-01-12';
@@ -0,0 +1,9 @@
1
+ import { AxiosInstance } from 'axios';
2
+ import VanillaPayInternational from '../vanilla-pay-international';
3
+ export declare class Service {
4
+ protected httpClient: AxiosInstance;
5
+ protected vanillaPay: VanillaPayInternational;
6
+ constructor(httpClient: AxiosInstance, vanillaPay: VanillaPayInternational);
7
+ setAccessToken(accessToken: string): void;
8
+ private setOptions;
9
+ }
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Service = void 0;
4
+ class Service {
5
+ constructor(httpClient, vanillaPay) {
6
+ this.httpClient = httpClient;
7
+ this.setOptions();
8
+ this.vanillaPay = vanillaPay;
9
+ }
10
+ setAccessToken(accessToken) {
11
+ this.httpClient.defaults.headers.common["Authorization"] = `${accessToken}`;
12
+ }
13
+ setOptions() {
14
+ this.httpClient.defaults.headers.common['Date'] = new Date().toUTCString();
15
+ this.httpClient.defaults.headers.common['Cache-Control'] = 'no-cache';
16
+ }
17
+ }
18
+ exports.Service = Service;
@@ -0,0 +1,42 @@
1
+ export type Environment = 'PREPROD' | 'PROD';
2
+ export type PaymentMethod = 'international' | 'mobile_money';
3
+ export interface ApiResponse<T> {
4
+ CodeRetour: number;
5
+ DescRetour: string;
6
+ DetailRetour: string;
7
+ Data: T;
8
+ }
9
+ export interface AuthResponse {
10
+ Token: string;
11
+ }
12
+ export interface PaymentRequest {
13
+ amount: number;
14
+ reference: string;
15
+ currency: 'MGA' | 'EUR';
16
+ cart: string;
17
+ callbackUrl: string;
18
+ redirectUrl: string;
19
+ paymentMethod: PaymentMethod;
20
+ }
21
+ export interface PaymentResponse {
22
+ id: string;
23
+ url: string;
24
+ }
25
+ export declare enum PaymentStatus {
26
+ INITIATED = "INITIATED",
27
+ SUCCESS = "SUCCESS",
28
+ PENDING = "PENDING",
29
+ FAILED = "FAILED"
30
+ }
31
+ export interface PaymentDetails {
32
+ id?: string;
33
+ amount: number;
34
+ receivedAmount?: number;
35
+ reference: string;
36
+ vpiReference: string;
37
+ status: PaymentStatus;
38
+ cart?: string;
39
+ paymentMethod: PaymentMethod;
40
+ phoneNumber?: string;
41
+ mobileMoneyReference?: string;
42
+ }
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PaymentStatus = void 0;
4
+ var PaymentStatus;
5
+ (function (PaymentStatus) {
6
+ PaymentStatus["INITIATED"] = "INITIATED";
7
+ PaymentStatus["SUCCESS"] = "SUCCESS";
8
+ PaymentStatus["PENDING"] = "PENDING";
9
+ PaymentStatus["FAILED"] = "FAILED";
10
+ })(PaymentStatus || (exports.PaymentStatus = PaymentStatus = {}));
@@ -0,0 +1,6 @@
1
+ import { PaymentDetails } from "./type";
2
+ export declare class Util {
3
+ static formatAndValidatePayload(vpiSignature: string, body: any, keySecret: string): PaymentDetails | false;
4
+ static formatPaymentData(data: any): PaymentDetails;
5
+ private static hashData;
6
+ }
@@ -0,0 +1,41 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Util = void 0;
4
+ const crypto_1 = require("crypto");
5
+ class Util {
6
+ static formatAndValidatePayload(vpiSignature, body, keySecret) {
7
+ const bodyString = JSON.stringify(body);
8
+ const hashedData = Util.hashData(keySecret, bodyString);
9
+ const isValid = hashedData.toLowerCase() === vpiSignature.toLowerCase();
10
+ if (!isValid)
11
+ return false;
12
+ return {
13
+ vpiReference: body.reference_VPI,
14
+ cart: body.panier,
15
+ reference: body.reference,
16
+ amount: body.montant,
17
+ status: body.etat,
18
+ paymentMethod: body.referenceMM ? 'mobile_money' : 'international',
19
+ phoneNumber: body.initiateur,
20
+ mobileMoneyReference: body.referenceMM
21
+ };
22
+ }
23
+ static formatPaymentData(data) {
24
+ return {
25
+ vpiReference: data.reference_VPI,
26
+ cart: data.panier,
27
+ reference: data.reference,
28
+ amount: data.montant,
29
+ receivedAmount: data.montantRecu,
30
+ status: data.etat,
31
+ paymentMethod: data.referenceMM ? 'mobile_money' : 'international',
32
+ phoneNumber: data.initiateur,
33
+ mobileMoneyReference: data.referenceMM
34
+ };
35
+ }
36
+ static hashData(key, payload) {
37
+ const hashedData = (0, crypto_1.createHmac)('SHA256', key).update(payload).digest('hex');
38
+ return hashedData;
39
+ }
40
+ }
41
+ exports.Util = Util;
@@ -0,0 +1,12 @@
1
+ import PaymentService from "./payment";
2
+ import AuthService from "./auth";
3
+ import { Environment } from "./shared/type";
4
+ declare class VanillaPayInternational {
5
+ payment: PaymentService;
6
+ auth: AuthService;
7
+ environment: Environment;
8
+ constructor(environment: Environment);
9
+ setAccessToken(accessToken: string): void;
10
+ private getUrl;
11
+ }
12
+ export default VanillaPayInternational;
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const axios_1 = __importDefault(require("axios"));
7
+ const payment_1 = __importDefault(require("./payment"));
8
+ const auth_1 = __importDefault(require("./auth"));
9
+ const const_1 = require("./shared/const");
10
+ class VanillaPayInternational {
11
+ constructor(environment) {
12
+ const client = axios_1.default.create({
13
+ baseURL: this.getUrl(environment),
14
+ headers: {
15
+ 'Accept': '*/*',
16
+ 'VPI-VERSION': const_1.CURRENT_VPI_VERSION
17
+ }
18
+ });
19
+ this.environment = environment;
20
+ this.payment = new payment_1.default(client, this);
21
+ this.auth = new auth_1.default(client, this);
22
+ }
23
+ setAccessToken(accessToken) {
24
+ this.payment.setAccessToken(accessToken);
25
+ }
26
+ getUrl(environment) {
27
+ return environment === 'PREPROD' ? const_1.PREPROD_URL : const_1.PROD_URL;
28
+ }
29
+ }
30
+ exports.default = VanillaPayInternational;
package/package.json ADDED
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "@mandarvl/vanilla-pay-international",
3
+ "version": "1.0.0",
4
+ "description": "A Node.JS package for Vanilla Pay International API",
5
+ "private": false,
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "scripts": {
9
+ "clean": "rm -rf dist",
10
+ "build": "npm run clean && tsc",
11
+ "prepublishOnly": "npm run build",
12
+ "deploy": "npm publish --access public"
13
+ },
14
+ "author": "Manda Ravalison",
15
+ "keywords": [
16
+ "vanilla pay",
17
+ "vanilla pay international",
18
+ "online payment",
19
+ "visa",
20
+ "mastercard",
21
+ "paypal",
22
+ "mobile money",
23
+ "mvola",
24
+ "orange money",
25
+ "airtel money"
26
+ ],
27
+ "files": ["dist"],
28
+ "license": "MIT",
29
+ "dependencies": {
30
+ "axios": "^1.4.0",
31
+ "crypto-js": "^4.1.1"
32
+ },
33
+ "devDependencies": {
34
+ "dotenv": "^16.4.5",
35
+ "@types/dotenv": "^8.2.0",
36
+ "@types/crypto-js": "^4.1.2",
37
+ "@types/node": "^20.1.4",
38
+ "typescript": "^5.0.4"
39
+ },
40
+ "exports": {
41
+ ".": {
42
+ "types": "./dist/index.d.ts",
43
+ "require": "./dist/index.js",
44
+ "import": "./dist/index.js"
45
+ }
46
+ }
47
+ }