@rajeev02/payments 0.1.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/lib/cards/index.d.ts +65 -0
- package/lib/cards/index.d.ts.map +1 -0
- package/lib/cards/index.js +83 -0
- package/lib/cards/index.js.map +1 -0
- package/lib/index.d.ts +15 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +25 -0
- package/lib/index.js.map +1 -0
- package/lib/upi/index.d.ts +69 -0
- package/lib/upi/index.d.ts.map +1 -0
- package/lib/upi/index.js +64 -0
- package/lib/upi/index.js.map +1 -0
- package/lib/wallet/index.d.ts +44 -0
- package/lib/wallet/index.d.ts.map +1 -0
- package/lib/wallet/index.js +46 -0
- package/lib/wallet/index.js.map +1 -0
- package/package.json +51 -0
- package/src/cards/index.ts +132 -0
- package/src/index.ts +37 -0
- package/src/upi/index.ts +115 -0
- package/src/wallet/index.ts +82 -0
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @rajeev02/payments — Cards & Subscriptions
|
|
3
|
+
* Card tokenization (RBI compliant), saved cards, recurring billing
|
|
4
|
+
*/
|
|
5
|
+
export interface CardInfo {
|
|
6
|
+
/** Tokenized card reference (never store raw card numbers) */
|
|
7
|
+
tokenRef: string;
|
|
8
|
+
/** Last 4 digits for display */
|
|
9
|
+
last4: string;
|
|
10
|
+
/** Card network */
|
|
11
|
+
network: "visa" | "mastercard" | "rupay" | "amex" | "dinersclub";
|
|
12
|
+
/** Card type */
|
|
13
|
+
type: "credit" | "debit" | "prepaid";
|
|
14
|
+
/** Issuing bank name */
|
|
15
|
+
issuerBank?: string;
|
|
16
|
+
/** Expiry month */
|
|
17
|
+
expiryMonth: number;
|
|
18
|
+
/** Expiry year */
|
|
19
|
+
expiryYear: number;
|
|
20
|
+
/** Cardholder name */
|
|
21
|
+
holderName?: string;
|
|
22
|
+
/** Whether card supports recurring/mandate */
|
|
23
|
+
supportsRecurring: boolean;
|
|
24
|
+
}
|
|
25
|
+
export interface SubscriptionPlan {
|
|
26
|
+
id: string;
|
|
27
|
+
name: string;
|
|
28
|
+
description?: string;
|
|
29
|
+
amount: number;
|
|
30
|
+
currency: string;
|
|
31
|
+
interval: "daily" | "weekly" | "monthly" | "quarterly" | "yearly";
|
|
32
|
+
intervalCount: number;
|
|
33
|
+
trialDays?: number;
|
|
34
|
+
features?: string[];
|
|
35
|
+
}
|
|
36
|
+
export interface Subscription {
|
|
37
|
+
id: string;
|
|
38
|
+
planId: string;
|
|
39
|
+
customerId: string;
|
|
40
|
+
status: "active" | "paused" | "cancelled" | "past_due" | "trialing" | "expired";
|
|
41
|
+
currentPeriodStart: string;
|
|
42
|
+
currentPeriodEnd: string;
|
|
43
|
+
cancelledAt?: string;
|
|
44
|
+
paymentMethod: "card" | "upi_mandate" | "emandate";
|
|
45
|
+
tokenRef?: string;
|
|
46
|
+
nextBillingDate: string;
|
|
47
|
+
amount: number;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Detect card network from number prefix (first 6 digits)
|
|
51
|
+
*/
|
|
52
|
+
export declare function detectCardNetwork(cardNumber: string): CardInfo["network"] | null;
|
|
53
|
+
/**
|
|
54
|
+
* Format card number with spaces: 4111111111111111 → 4111 1111 1111 1111
|
|
55
|
+
*/
|
|
56
|
+
export declare function formatCardNumber(cardNumber: string): string;
|
|
57
|
+
/**
|
|
58
|
+
* Validate card number using Luhn algorithm
|
|
59
|
+
*/
|
|
60
|
+
export declare function validateCardNumber(cardNumber: string): boolean;
|
|
61
|
+
/**
|
|
62
|
+
* Calculate subscription renewal dates
|
|
63
|
+
*/
|
|
64
|
+
export declare function getNextBillingDate(plan: SubscriptionPlan, currentDate?: Date): Date;
|
|
65
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cards/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,WAAW,QAAQ;IACvB,8DAA8D;IAC9D,QAAQ,EAAE,MAAM,CAAC;IACjB,gCAAgC;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,mBAAmB;IACnB,OAAO,EAAE,MAAM,GAAG,YAAY,GAAG,OAAO,GAAG,MAAM,GAAG,YAAY,CAAC;IACjE,gBAAgB;IAChB,IAAI,EAAE,QAAQ,GAAG,OAAO,GAAG,SAAS,CAAC;IACrC,wBAAwB;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,mBAAmB;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,kBAAkB;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,sBAAsB;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,8CAA8C;IAC9C,iBAAiB,EAAE,OAAO,CAAC;CAC5B;AAED,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,OAAO,GAAG,QAAQ,GAAG,SAAS,GAAG,WAAW,GAAG,QAAQ,CAAC;IAClE,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;CACrB;AAED,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EACF,QAAQ,GACR,QAAQ,GACR,WAAW,GACX,UAAU,GACV,UAAU,GACV,SAAS,CAAC;IACd,kBAAkB,EAAE,MAAM,CAAC;IAC3B,gBAAgB,EAAE,MAAM,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,GAAG,aAAa,GAAG,UAAU,CAAC;IACnD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,eAAe,EAAE,MAAM,CAAC;IACxB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAC/B,UAAU,EAAE,MAAM,GACjB,QAAQ,CAAC,SAAS,CAAC,GAAG,IAAI,CAQ5B;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAM3D;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAgB9D;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,gBAAgB,EACtB,WAAW,GAAE,IAAiB,GAC7B,IAAI,CAoBN"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @rajeev02/payments — Cards & Subscriptions
|
|
4
|
+
* Card tokenization (RBI compliant), saved cards, recurring billing
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.detectCardNetwork = detectCardNetwork;
|
|
8
|
+
exports.formatCardNumber = formatCardNumber;
|
|
9
|
+
exports.validateCardNumber = validateCardNumber;
|
|
10
|
+
exports.getNextBillingDate = getNextBillingDate;
|
|
11
|
+
/**
|
|
12
|
+
* Detect card network from number prefix (first 6 digits)
|
|
13
|
+
*/
|
|
14
|
+
function detectCardNetwork(cardNumber) {
|
|
15
|
+
const digits = cardNumber.replace(/\s/g, "");
|
|
16
|
+
if (digits.startsWith("4"))
|
|
17
|
+
return "visa";
|
|
18
|
+
if (/^5[1-5]/.test(digits) || /^2[2-7]/.test(digits))
|
|
19
|
+
return "mastercard";
|
|
20
|
+
if (/^6(0|5|52[12]|53[89]|5[4-9]|[6-9])/.test(digits))
|
|
21
|
+
return "rupay";
|
|
22
|
+
if (/^3[47]/.test(digits))
|
|
23
|
+
return "amex";
|
|
24
|
+
if (/^3(0[0-5]|[68])/.test(digits))
|
|
25
|
+
return "dinersclub";
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Format card number with spaces: 4111111111111111 → 4111 1111 1111 1111
|
|
30
|
+
*/
|
|
31
|
+
function formatCardNumber(cardNumber) {
|
|
32
|
+
const digits = cardNumber.replace(/\D/g, "");
|
|
33
|
+
if (detectCardNetwork(digits) === "amex") {
|
|
34
|
+
return digits.replace(/(\d{4})(\d{6})(\d{5})/, "$1 $2 $3");
|
|
35
|
+
}
|
|
36
|
+
return digits.replace(/(\d{4})/g, "$1 ").trim();
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Validate card number using Luhn algorithm
|
|
40
|
+
*/
|
|
41
|
+
function validateCardNumber(cardNumber) {
|
|
42
|
+
const digits = cardNumber.replace(/\D/g, "");
|
|
43
|
+
if (digits.length < 13 || digits.length > 19)
|
|
44
|
+
return false;
|
|
45
|
+
let sum = 0;
|
|
46
|
+
let isEven = false;
|
|
47
|
+
for (let i = digits.length - 1; i >= 0; i--) {
|
|
48
|
+
let d = parseInt(digits[i], 10);
|
|
49
|
+
if (isEven) {
|
|
50
|
+
d *= 2;
|
|
51
|
+
if (d > 9)
|
|
52
|
+
d -= 9;
|
|
53
|
+
}
|
|
54
|
+
sum += d;
|
|
55
|
+
isEven = !isEven;
|
|
56
|
+
}
|
|
57
|
+
return sum % 10 === 0;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Calculate subscription renewal dates
|
|
61
|
+
*/
|
|
62
|
+
function getNextBillingDate(plan, currentDate = new Date()) {
|
|
63
|
+
const next = new Date(currentDate);
|
|
64
|
+
switch (plan.interval) {
|
|
65
|
+
case "daily":
|
|
66
|
+
next.setDate(next.getDate() + plan.intervalCount);
|
|
67
|
+
break;
|
|
68
|
+
case "weekly":
|
|
69
|
+
next.setDate(next.getDate() + 7 * plan.intervalCount);
|
|
70
|
+
break;
|
|
71
|
+
case "monthly":
|
|
72
|
+
next.setMonth(next.getMonth() + plan.intervalCount);
|
|
73
|
+
break;
|
|
74
|
+
case "quarterly":
|
|
75
|
+
next.setMonth(next.getMonth() + 3 * plan.intervalCount);
|
|
76
|
+
break;
|
|
77
|
+
case "yearly":
|
|
78
|
+
next.setFullYear(next.getFullYear() + plan.intervalCount);
|
|
79
|
+
break;
|
|
80
|
+
}
|
|
81
|
+
return next;
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cards/index.ts"],"names":[],"mappings":";AAAA;;;GAGG;;AA0DH,8CAUC;AAKD,4CAMC;AAKD,gDAgBC;AAKD,gDAuBC;AAzED;;GAEG;AACH,SAAgB,iBAAiB,CAC/B,UAAkB;IAElB,MAAM,MAAM,GAAG,UAAU,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IAC7C,IAAI,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,OAAO,MAAM,CAAC;IAC1C,IAAI,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC;QAAE,OAAO,YAAY,CAAC;IAC1E,IAAI,oCAAoC,CAAC,IAAI,CAAC,MAAM,CAAC;QAAE,OAAO,OAAO,CAAC;IACtE,IAAI,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC;QAAE,OAAO,MAAM,CAAC;IACzC,IAAI,iBAAiB,CAAC,IAAI,CAAC,MAAM,CAAC;QAAE,OAAO,YAAY,CAAC;IACxD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;GAEG;AACH,SAAgB,gBAAgB,CAAC,UAAkB;IACjD,MAAM,MAAM,GAAG,UAAU,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IAC7C,IAAI,iBAAiB,CAAC,MAAM,CAAC,KAAK,MAAM,EAAE,CAAC;QACzC,OAAO,MAAM,CAAC,OAAO,CAAC,uBAAuB,EAAE,UAAU,CAAC,CAAC;IAC7D,CAAC;IACD,OAAO,MAAM,CAAC,OAAO,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC;AAClD,CAAC;AAED;;GAEG;AACH,SAAgB,kBAAkB,CAAC,UAAkB;IACnD,MAAM,MAAM,GAAG,UAAU,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IAC7C,IAAI,MAAM,CAAC,MAAM,GAAG,EAAE,IAAI,MAAM,CAAC,MAAM,GAAG,EAAE;QAAE,OAAO,KAAK,CAAC;IAE3D,IAAI,GAAG,GAAG,CAAC,CAAC;IACZ,IAAI,MAAM,GAAG,KAAK,CAAC;IACnB,KAAK,IAAI,CAAC,GAAG,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QAC5C,IAAI,CAAC,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAChC,IAAI,MAAM,EAAE,CAAC;YACX,CAAC,IAAI,CAAC,CAAC;YACP,IAAI,CAAC,GAAG,CAAC;gBAAE,CAAC,IAAI,CAAC,CAAC;QACpB,CAAC;QACD,GAAG,IAAI,CAAC,CAAC;QACT,MAAM,GAAG,CAAC,MAAM,CAAC;IACnB,CAAC;IACD,OAAO,GAAG,GAAG,EAAE,KAAK,CAAC,CAAC;AACxB,CAAC;AAED;;GAEG;AACH,SAAgB,kBAAkB,CAChC,IAAsB,EACtB,cAAoB,IAAI,IAAI,EAAE;IAE9B,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,WAAW,CAAC,CAAC;IACnC,QAAQ,IAAI,CAAC,QAAQ,EAAE,CAAC;QACtB,KAAK,OAAO;YACV,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,aAAa,CAAC,CAAC;YAClD,MAAM;QACR,KAAK,QAAQ;YACX,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,CAAC,GAAG,IAAI,CAAC,aAAa,CAAC,CAAC;YACtD,MAAM;QACR,KAAK,SAAS;YACZ,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,EAAE,GAAG,IAAI,CAAC,aAAa,CAAC,CAAC;YACpD,MAAM;QACR,KAAK,WAAW;YACd,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,EAAE,GAAG,CAAC,GAAG,IAAI,CAAC,aAAa,CAAC,CAAC;YACxD,MAAM;QACR,KAAK,QAAQ;YACX,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,WAAW,EAAE,GAAG,IAAI,CAAC,aAAa,CAAC,CAAC;YAC1D,MAAM;IACV,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
|
package/lib/index.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @rajeev02/payments
|
|
3
|
+
* Payments Abstraction SDK — India focused
|
|
4
|
+
* UPI, wallets, cards, subscriptions
|
|
5
|
+
*
|
|
6
|
+
* @author Rajeev Kumar Joshi
|
|
7
|
+
* @license MIT
|
|
8
|
+
*/
|
|
9
|
+
export { generateUpiUri, generateUpiQrData, validateVpa, getPspName, UPI_PSP_HANDLES, } from "./upi";
|
|
10
|
+
export type { UpiConfig, UpiPaymentRequest, UpiPaymentResult, UpiMandateRequest, } from "./upi";
|
|
11
|
+
export { WalletManager } from "./wallet";
|
|
12
|
+
export type { WalletProvider, WalletConfig, WalletPaymentRequest, WalletPaymentResult, } from "./wallet";
|
|
13
|
+
export { detectCardNetwork, formatCardNumber, validateCardNumber, getNextBillingDate, } from "./cards";
|
|
14
|
+
export type { CardInfo, SubscriptionPlan, Subscription } from "./cards";
|
|
15
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EACL,cAAc,EACd,iBAAiB,EACjB,WAAW,EACX,UAAU,EACV,eAAe,GAChB,MAAM,OAAO,CAAC;AACf,YAAY,EACV,SAAS,EACT,iBAAiB,EACjB,gBAAgB,EAChB,iBAAiB,GAClB,MAAM,OAAO,CAAC;AAEf,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,YAAY,EACV,cAAc,EACd,YAAY,EACZ,oBAAoB,EACpB,mBAAmB,GACpB,MAAM,UAAU,CAAC;AAElB,OAAO,EACL,iBAAiB,EACjB,gBAAgB,EAChB,kBAAkB,EAClB,kBAAkB,GACnB,MAAM,SAAS,CAAC;AACjB,YAAY,EAAE,QAAQ,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC"}
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getNextBillingDate = exports.validateCardNumber = exports.formatCardNumber = exports.detectCardNetwork = exports.WalletManager = exports.UPI_PSP_HANDLES = exports.getPspName = exports.validateVpa = exports.generateUpiQrData = exports.generateUpiUri = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* @rajeev02/payments
|
|
6
|
+
* Payments Abstraction SDK — India focused
|
|
7
|
+
* UPI, wallets, cards, subscriptions
|
|
8
|
+
*
|
|
9
|
+
* @author Rajeev Kumar Joshi
|
|
10
|
+
* @license MIT
|
|
11
|
+
*/
|
|
12
|
+
var upi_1 = require("./upi");
|
|
13
|
+
Object.defineProperty(exports, "generateUpiUri", { enumerable: true, get: function () { return upi_1.generateUpiUri; } });
|
|
14
|
+
Object.defineProperty(exports, "generateUpiQrData", { enumerable: true, get: function () { return upi_1.generateUpiQrData; } });
|
|
15
|
+
Object.defineProperty(exports, "validateVpa", { enumerable: true, get: function () { return upi_1.validateVpa; } });
|
|
16
|
+
Object.defineProperty(exports, "getPspName", { enumerable: true, get: function () { return upi_1.getPspName; } });
|
|
17
|
+
Object.defineProperty(exports, "UPI_PSP_HANDLES", { enumerable: true, get: function () { return upi_1.UPI_PSP_HANDLES; } });
|
|
18
|
+
var wallet_1 = require("./wallet");
|
|
19
|
+
Object.defineProperty(exports, "WalletManager", { enumerable: true, get: function () { return wallet_1.WalletManager; } });
|
|
20
|
+
var cards_1 = require("./cards");
|
|
21
|
+
Object.defineProperty(exports, "detectCardNetwork", { enumerable: true, get: function () { return cards_1.detectCardNetwork; } });
|
|
22
|
+
Object.defineProperty(exports, "formatCardNumber", { enumerable: true, get: function () { return cards_1.formatCardNumber; } });
|
|
23
|
+
Object.defineProperty(exports, "validateCardNumber", { enumerable: true, get: function () { return cards_1.validateCardNumber; } });
|
|
24
|
+
Object.defineProperty(exports, "getNextBillingDate", { enumerable: true, get: function () { return cards_1.getNextBillingDate; } });
|
|
25
|
+
//# sourceMappingURL=index.js.map
|
package/lib/index.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA;;;;;;;GAOG;AACH,6BAMe;AALb,qGAAA,cAAc,OAAA;AACd,wGAAA,iBAAiB,OAAA;AACjB,kGAAA,WAAW,OAAA;AACX,iGAAA,UAAU,OAAA;AACV,sGAAA,eAAe,OAAA;AASjB,mCAAyC;AAAhC,uGAAA,aAAa,OAAA;AAQtB,iCAKiB;AAJf,0GAAA,iBAAiB,OAAA;AACjB,yGAAA,gBAAgB,OAAA;AAChB,2GAAA,kBAAkB,OAAA;AAClB,2GAAA,kBAAkB,OAAA"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @rajeev02/payments — UPI Module
|
|
3
|
+
* UPI intent, collect request, QR code, autopay mandates
|
|
4
|
+
*/
|
|
5
|
+
export interface UpiConfig {
|
|
6
|
+
/** Merchant VPA (e.g., merchant@paytm) */
|
|
7
|
+
merchantVpa: string;
|
|
8
|
+
/** Merchant name shown in UPI apps */
|
|
9
|
+
merchantName: string;
|
|
10
|
+
/** Merchant category code */
|
|
11
|
+
mcc?: string;
|
|
12
|
+
/** Transaction reference prefix */
|
|
13
|
+
txnRefPrefix?: string;
|
|
14
|
+
}
|
|
15
|
+
export interface UpiPaymentRequest {
|
|
16
|
+
/** Amount in INR (paisa precision) */
|
|
17
|
+
amount: number;
|
|
18
|
+
/** Payment description */
|
|
19
|
+
note: string;
|
|
20
|
+
/** Unique transaction reference */
|
|
21
|
+
txnRef?: string;
|
|
22
|
+
/** Customer VPA (for collect requests) */
|
|
23
|
+
customerVpa?: string;
|
|
24
|
+
/** Order ID from your backend */
|
|
25
|
+
orderId: string;
|
|
26
|
+
}
|
|
27
|
+
export interface UpiPaymentResult {
|
|
28
|
+
success: boolean;
|
|
29
|
+
txnId?: string;
|
|
30
|
+
txnRef?: string;
|
|
31
|
+
responseCode?: string;
|
|
32
|
+
approvalRefNo?: string;
|
|
33
|
+
status: "success" | "failed" | "pending" | "cancelled";
|
|
34
|
+
error?: string;
|
|
35
|
+
}
|
|
36
|
+
export interface UpiMandateRequest {
|
|
37
|
+
/** Customer VPA */
|
|
38
|
+
customerVpa: string;
|
|
39
|
+
/** Maximum amount per debit */
|
|
40
|
+
maxAmount: number;
|
|
41
|
+
/** Frequency: DAILY, WEEKLY, FORTNIGHTLY, MONTHLY, BIMONTHLY, QUARTERLY, HALFYEARLY, YEARLY */
|
|
42
|
+
frequency: string;
|
|
43
|
+
/** Start date (ISO) */
|
|
44
|
+
startDate: string;
|
|
45
|
+
/** End date (ISO) */
|
|
46
|
+
endDate: string;
|
|
47
|
+
/** Purpose description */
|
|
48
|
+
purpose: string;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Generate UPI deep link URI for intent-based payment
|
|
52
|
+
* Works with all UPI apps (GPay, PhonePe, Paytm, etc.)
|
|
53
|
+
*/
|
|
54
|
+
export declare function generateUpiUri(config: UpiConfig, request: UpiPaymentRequest): string;
|
|
55
|
+
/**
|
|
56
|
+
* Generate UPI QR code data string
|
|
57
|
+
*/
|
|
58
|
+
export declare function generateUpiQrData(config: UpiConfig, request: UpiPaymentRequest): string;
|
|
59
|
+
/**
|
|
60
|
+
* Validate a UPI VPA format
|
|
61
|
+
* Format: username@psp (e.g., rajeev@paytm, user@okicici)
|
|
62
|
+
*/
|
|
63
|
+
export declare function validateVpa(vpa: string): boolean;
|
|
64
|
+
/**
|
|
65
|
+
* Known UPI PSP handles
|
|
66
|
+
*/
|
|
67
|
+
export declare const UPI_PSP_HANDLES: Record<string, string>;
|
|
68
|
+
export declare function getPspName(vpa: string): string | null;
|
|
69
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/upi/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,WAAW,SAAS;IACxB,0CAA0C;IAC1C,WAAW,EAAE,MAAM,CAAC;IACpB,sCAAsC;IACtC,YAAY,EAAE,MAAM,CAAC;IACrB,6BAA6B;IAC7B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,mCAAmC;IACnC,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,iBAAiB;IAChC,sCAAsC;IACtC,MAAM,EAAE,MAAM,CAAC;IACf,0BAA0B;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,mCAAmC;IACnC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,0CAA0C;IAC1C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iCAAiC;IACjC,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,SAAS,GAAG,QAAQ,GAAG,SAAS,GAAG,WAAW,CAAC;IACvD,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,iBAAiB;IAChC,mBAAmB;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,+BAA+B;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,+FAA+F;IAC/F,SAAS,EAAE,MAAM,CAAC;IAClB,uBAAuB;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,qBAAqB;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,0BAA0B;IAC1B,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAC5B,MAAM,EAAE,SAAS,EACjB,OAAO,EAAE,iBAAiB,GACzB,MAAM,CAcR;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,SAAS,EACjB,OAAO,EAAE,iBAAiB,GACzB,MAAM,CAER;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAEhD;AAED;;GAEG;AACH,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAWlD,CAAC;AAEF,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAGrD"}
|
package/lib/upi/index.js
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @rajeev02/payments — UPI Module
|
|
4
|
+
* UPI intent, collect request, QR code, autopay mandates
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.UPI_PSP_HANDLES = void 0;
|
|
8
|
+
exports.generateUpiUri = generateUpiUri;
|
|
9
|
+
exports.generateUpiQrData = generateUpiQrData;
|
|
10
|
+
exports.validateVpa = validateVpa;
|
|
11
|
+
exports.getPspName = getPspName;
|
|
12
|
+
/**
|
|
13
|
+
* Generate UPI deep link URI for intent-based payment
|
|
14
|
+
* Works with all UPI apps (GPay, PhonePe, Paytm, etc.)
|
|
15
|
+
*/
|
|
16
|
+
function generateUpiUri(config, request) {
|
|
17
|
+
const txnRef = request.txnRef || `${config.txnRefPrefix || "TXN"}${Date.now()}`;
|
|
18
|
+
const params = new URLSearchParams({
|
|
19
|
+
pa: config.merchantVpa,
|
|
20
|
+
pn: config.merchantName,
|
|
21
|
+
am: request.amount.toFixed(2),
|
|
22
|
+
cu: "INR",
|
|
23
|
+
tn: request.note,
|
|
24
|
+
tr: txnRef,
|
|
25
|
+
});
|
|
26
|
+
if (config.mcc)
|
|
27
|
+
params.set("mc", config.mcc);
|
|
28
|
+
if (request.orderId)
|
|
29
|
+
params.set("tid", request.orderId);
|
|
30
|
+
return `upi://pay?${params.toString()}`;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Generate UPI QR code data string
|
|
34
|
+
*/
|
|
35
|
+
function generateUpiQrData(config, request) {
|
|
36
|
+
return generateUpiUri(config, request);
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Validate a UPI VPA format
|
|
40
|
+
* Format: username@psp (e.g., rajeev@paytm, user@okicici)
|
|
41
|
+
*/
|
|
42
|
+
function validateVpa(vpa) {
|
|
43
|
+
return /^[a-zA-Z0-9._-]+@[a-zA-Z0-9]+$/.test(vpa);
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Known UPI PSP handles
|
|
47
|
+
*/
|
|
48
|
+
exports.UPI_PSP_HANDLES = {
|
|
49
|
+
"@paytm": "Paytm",
|
|
50
|
+
"@okicici": "Google Pay (ICICI)",
|
|
51
|
+
"@okhdfcbank": "Google Pay (HDFC)",
|
|
52
|
+
"@oksbi": "Google Pay (SBI)",
|
|
53
|
+
"@ybl": "PhonePe (YES Bank)",
|
|
54
|
+
"@ibl": "PhonePe (ICICI)",
|
|
55
|
+
"@axl": "PhonePe (Axis)",
|
|
56
|
+
"@apl": "Amazon Pay",
|
|
57
|
+
"@freecharge": "Freecharge",
|
|
58
|
+
"@upi": "BHIM",
|
|
59
|
+
};
|
|
60
|
+
function getPspName(vpa) {
|
|
61
|
+
const handle = "@" + vpa.split("@")[1];
|
|
62
|
+
return exports.UPI_PSP_HANDLES[handle] ?? null;
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/upi/index.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;AAuDH,wCAiBC;AAKD,8CAKC;AAMD,kCAEC;AAkBD,gCAGC;AA5DD;;;GAGG;AACH,SAAgB,cAAc,CAC5B,MAAiB,EACjB,OAA0B;IAE1B,MAAM,MAAM,GACV,OAAO,CAAC,MAAM,IAAI,GAAG,MAAM,CAAC,YAAY,IAAI,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;IACnE,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC;QACjC,EAAE,EAAE,MAAM,CAAC,WAAW;QACtB,EAAE,EAAE,MAAM,CAAC,YAAY;QACvB,EAAE,EAAE,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;QAC7B,EAAE,EAAE,KAAK;QACT,EAAE,EAAE,OAAO,CAAC,IAAI;QAChB,EAAE,EAAE,MAAM;KACX,CAAC,CAAC;IACH,IAAI,MAAM,CAAC,GAAG;QAAE,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC;IAC7C,IAAI,OAAO,CAAC,OAAO;QAAE,MAAM,CAAC,GAAG,CAAC,KAAK,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;IACxD,OAAO,aAAa,MAAM,CAAC,QAAQ,EAAE,EAAE,CAAC;AAC1C,CAAC;AAED;;GAEG;AACH,SAAgB,iBAAiB,CAC/B,MAAiB,EACjB,OAA0B;IAE1B,OAAO,cAAc,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AACzC,CAAC;AAED;;;GAGG;AACH,SAAgB,WAAW,CAAC,GAAW;IACrC,OAAO,gCAAgC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACpD,CAAC;AAED;;GAEG;AACU,QAAA,eAAe,GAA2B;IACrD,QAAQ,EAAE,OAAO;IACjB,UAAU,EAAE,oBAAoB;IAChC,aAAa,EAAE,mBAAmB;IAClC,QAAQ,EAAE,kBAAkB;IAC5B,MAAM,EAAE,oBAAoB;IAC5B,MAAM,EAAE,iBAAiB;IACzB,MAAM,EAAE,gBAAgB;IACxB,MAAM,EAAE,YAAY;IACpB,aAAa,EAAE,YAAY;IAC3B,MAAM,EAAE,MAAM;CACf,CAAC;AAEF,SAAgB,UAAU,CAAC,GAAW;IACpC,MAAM,MAAM,GAAG,GAAG,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IACvC,OAAO,uBAAe,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC;AACzC,CAAC"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @rajeev02/payments — Wallet Module
|
|
3
|
+
* Abstraction over PhonePe, Paytm, Amazon Pay, Freecharge wallets
|
|
4
|
+
*/
|
|
5
|
+
export type WalletProvider = "paytm" | "phonepe" | "amazon_pay" | "freecharge" | "mobikwik" | "jio_pay";
|
|
6
|
+
export interface WalletConfig {
|
|
7
|
+
provider: WalletProvider;
|
|
8
|
+
merchantId: string;
|
|
9
|
+
merchantKey?: string;
|
|
10
|
+
environment: "sandbox" | "production";
|
|
11
|
+
callbackUrl: string;
|
|
12
|
+
}
|
|
13
|
+
export interface WalletPaymentRequest {
|
|
14
|
+
orderId: string;
|
|
15
|
+
amount: number;
|
|
16
|
+
customerId: string;
|
|
17
|
+
customerPhone?: string;
|
|
18
|
+
customerEmail?: string;
|
|
19
|
+
description?: string;
|
|
20
|
+
}
|
|
21
|
+
export interface WalletPaymentResult {
|
|
22
|
+
success: boolean;
|
|
23
|
+
provider: WalletProvider;
|
|
24
|
+
txnId?: string;
|
|
25
|
+
orderId: string;
|
|
26
|
+
status: "success" | "failed" | "pending" | "cancelled";
|
|
27
|
+
walletBalance?: number;
|
|
28
|
+
error?: string;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Wallet Manager — handles payment flow across wallet providers
|
|
32
|
+
*/
|
|
33
|
+
export declare class WalletManager {
|
|
34
|
+
private configs;
|
|
35
|
+
/** Register a wallet provider */
|
|
36
|
+
register(config: WalletConfig): void;
|
|
37
|
+
/** Get all registered wallet providers */
|
|
38
|
+
getAvailableWallets(): WalletProvider[];
|
|
39
|
+
/** Check if a wallet is registered */
|
|
40
|
+
isAvailable(provider: WalletProvider): boolean;
|
|
41
|
+
/** Generate checkout URL/payload for a wallet */
|
|
42
|
+
generateCheckout(provider: WalletProvider, request: WalletPaymentRequest): Record<string, unknown> | null;
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/wallet/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,MAAM,cAAc,GACtB,OAAO,GACP,SAAS,GACT,YAAY,GACZ,YAAY,GACZ,UAAU,GACV,SAAS,CAAC;AAEd,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,cAAc,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,SAAS,GAAG,YAAY,CAAC;IACtC,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,cAAc,CAAC;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,SAAS,GAAG,QAAQ,GAAG,SAAS,GAAG,WAAW,CAAC;IACvD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,OAAO,CAAgD;IAE/D,iCAAiC;IACjC,QAAQ,CAAC,MAAM,EAAE,YAAY,GAAG,IAAI;IAIpC,0CAA0C;IAC1C,mBAAmB,IAAI,cAAc,EAAE;IAIvC,sCAAsC;IACtC,WAAW,CAAC,QAAQ,EAAE,cAAc,GAAG,OAAO;IAI9C,iDAAiD;IACjD,gBAAgB,CACd,QAAQ,EAAE,cAAc,EACxB,OAAO,EAAE,oBAAoB,GAC5B,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;CAgBlC"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @rajeev02/payments — Wallet Module
|
|
4
|
+
* Abstraction over PhonePe, Paytm, Amazon Pay, Freecharge wallets
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.WalletManager = void 0;
|
|
8
|
+
/**
|
|
9
|
+
* Wallet Manager — handles payment flow across wallet providers
|
|
10
|
+
*/
|
|
11
|
+
class WalletManager {
|
|
12
|
+
constructor() {
|
|
13
|
+
this.configs = new Map();
|
|
14
|
+
}
|
|
15
|
+
/** Register a wallet provider */
|
|
16
|
+
register(config) {
|
|
17
|
+
this.configs.set(config.provider, config);
|
|
18
|
+
}
|
|
19
|
+
/** Get all registered wallet providers */
|
|
20
|
+
getAvailableWallets() {
|
|
21
|
+
return Array.from(this.configs.keys());
|
|
22
|
+
}
|
|
23
|
+
/** Check if a wallet is registered */
|
|
24
|
+
isAvailable(provider) {
|
|
25
|
+
return this.configs.has(provider);
|
|
26
|
+
}
|
|
27
|
+
/** Generate checkout URL/payload for a wallet */
|
|
28
|
+
generateCheckout(provider, request) {
|
|
29
|
+
const config = this.configs.get(provider);
|
|
30
|
+
if (!config)
|
|
31
|
+
return null;
|
|
32
|
+
return {
|
|
33
|
+
provider,
|
|
34
|
+
merchantId: config.merchantId,
|
|
35
|
+
environment: config.environment,
|
|
36
|
+
callbackUrl: config.callbackUrl,
|
|
37
|
+
orderId: request.orderId,
|
|
38
|
+
amount: request.amount,
|
|
39
|
+
customerId: request.customerId,
|
|
40
|
+
phone: request.customerPhone,
|
|
41
|
+
email: request.customerEmail,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
exports.WalletManager = WalletManager;
|
|
46
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/wallet/index.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;AAqCH;;GAEG;AACH,MAAa,aAAa;IAA1B;QACU,YAAO,GAAsC,IAAI,GAAG,EAAE,CAAC;IAqCjE,CAAC;IAnCC,iCAAiC;IACjC,QAAQ,CAAC,MAAoB;QAC3B,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IAC5C,CAAC;IAED,0CAA0C;IAC1C,mBAAmB;QACjB,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;IACzC,CAAC;IAED,sCAAsC;IACtC,WAAW,CAAC,QAAwB;QAClC,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IACpC,CAAC;IAED,iDAAiD;IACjD,gBAAgB,CACd,QAAwB,EACxB,OAA6B;QAE7B,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAC1C,IAAI,CAAC,MAAM;YAAE,OAAO,IAAI,CAAC;QAEzB,OAAO;YACL,QAAQ;YACR,UAAU,EAAE,MAAM,CAAC,UAAU;YAC7B,WAAW,EAAE,MAAM,CAAC,WAAW;YAC/B,WAAW,EAAE,MAAM,CAAC,WAAW;YAC/B,OAAO,EAAE,OAAO,CAAC,OAAO;YACxB,MAAM,EAAE,OAAO,CAAC,MAAM;YACtB,UAAU,EAAE,OAAO,CAAC,UAAU;YAC9B,KAAK,EAAE,OAAO,CAAC,aAAa;YAC5B,KAAK,EAAE,OAAO,CAAC,aAAa;SAC7B,CAAC;IACJ,CAAC;CACF;AAtCD,sCAsCC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@rajeev02/payments",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Payments Abstraction SDK — UPI, wallets, cards, subscriptions, split payments (India-focused)",
|
|
5
|
+
"main": "lib/index.js",
|
|
6
|
+
"author": "Rajeev Kumar Joshi <rajeevjoshi91@gmail.com> (https://rajeev02.github.io)",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"types": "lib/index.d.ts",
|
|
9
|
+
"scripts": {
|
|
10
|
+
"build": "tsc",
|
|
11
|
+
"clean": "rm -rf lib",
|
|
12
|
+
"prepublishOnly": "npm run build"
|
|
13
|
+
},
|
|
14
|
+
"keywords": [
|
|
15
|
+
"react-native",
|
|
16
|
+
"payments",
|
|
17
|
+
"upi",
|
|
18
|
+
"wallets",
|
|
19
|
+
"india"
|
|
20
|
+
],
|
|
21
|
+
"repository": {
|
|
22
|
+
"type": "git",
|
|
23
|
+
"url": "https://github.com/Rajeev02/rajeev-sdk",
|
|
24
|
+
"directory": "packages/payments"
|
|
25
|
+
},
|
|
26
|
+
"homepage": "https://github.com/Rajeev02/rajeev-sdk#readme",
|
|
27
|
+
"bugs": {
|
|
28
|
+
"url": "https://github.com/Rajeev02/rajeev-sdk/issues"
|
|
29
|
+
},
|
|
30
|
+
"files": [
|
|
31
|
+
"lib/",
|
|
32
|
+
"src/",
|
|
33
|
+
"README.md"
|
|
34
|
+
],
|
|
35
|
+
"publishConfig": {
|
|
36
|
+
"access": "public"
|
|
37
|
+
},
|
|
38
|
+
"peerDependencies": {
|
|
39
|
+
"react": ">=18.3.0",
|
|
40
|
+
"react-native": ">=0.84.0"
|
|
41
|
+
},
|
|
42
|
+
"peerDependenciesMeta": {
|
|
43
|
+
"react-native": {
|
|
44
|
+
"optional": true
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
"devDependencies": {
|
|
48
|
+
"@types/react": "^19.0.0",
|
|
49
|
+
"typescript": "^5.4.0"
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @rajeev02/payments — Cards & Subscriptions
|
|
3
|
+
* Card tokenization (RBI compliant), saved cards, recurring billing
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export interface CardInfo {
|
|
7
|
+
/** Tokenized card reference (never store raw card numbers) */
|
|
8
|
+
tokenRef: string;
|
|
9
|
+
/** Last 4 digits for display */
|
|
10
|
+
last4: string;
|
|
11
|
+
/** Card network */
|
|
12
|
+
network: "visa" | "mastercard" | "rupay" | "amex" | "dinersclub";
|
|
13
|
+
/** Card type */
|
|
14
|
+
type: "credit" | "debit" | "prepaid";
|
|
15
|
+
/** Issuing bank name */
|
|
16
|
+
issuerBank?: string;
|
|
17
|
+
/** Expiry month */
|
|
18
|
+
expiryMonth: number;
|
|
19
|
+
/** Expiry year */
|
|
20
|
+
expiryYear: number;
|
|
21
|
+
/** Cardholder name */
|
|
22
|
+
holderName?: string;
|
|
23
|
+
/** Whether card supports recurring/mandate */
|
|
24
|
+
supportsRecurring: boolean;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface SubscriptionPlan {
|
|
28
|
+
id: string;
|
|
29
|
+
name: string;
|
|
30
|
+
description?: string;
|
|
31
|
+
amount: number;
|
|
32
|
+
currency: string;
|
|
33
|
+
interval: "daily" | "weekly" | "monthly" | "quarterly" | "yearly";
|
|
34
|
+
intervalCount: number;
|
|
35
|
+
trialDays?: number;
|
|
36
|
+
features?: string[];
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface Subscription {
|
|
40
|
+
id: string;
|
|
41
|
+
planId: string;
|
|
42
|
+
customerId: string;
|
|
43
|
+
status:
|
|
44
|
+
| "active"
|
|
45
|
+
| "paused"
|
|
46
|
+
| "cancelled"
|
|
47
|
+
| "past_due"
|
|
48
|
+
| "trialing"
|
|
49
|
+
| "expired";
|
|
50
|
+
currentPeriodStart: string;
|
|
51
|
+
currentPeriodEnd: string;
|
|
52
|
+
cancelledAt?: string;
|
|
53
|
+
paymentMethod: "card" | "upi_mandate" | "emandate";
|
|
54
|
+
tokenRef?: string;
|
|
55
|
+
nextBillingDate: string;
|
|
56
|
+
amount: number;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Detect card network from number prefix (first 6 digits)
|
|
61
|
+
*/
|
|
62
|
+
export function detectCardNetwork(
|
|
63
|
+
cardNumber: string,
|
|
64
|
+
): CardInfo["network"] | null {
|
|
65
|
+
const digits = cardNumber.replace(/\s/g, "");
|
|
66
|
+
if (digits.startsWith("4")) return "visa";
|
|
67
|
+
if (/^5[1-5]/.test(digits) || /^2[2-7]/.test(digits)) return "mastercard";
|
|
68
|
+
if (/^6(0|5|52[12]|53[89]|5[4-9]|[6-9])/.test(digits)) return "rupay";
|
|
69
|
+
if (/^3[47]/.test(digits)) return "amex";
|
|
70
|
+
if (/^3(0[0-5]|[68])/.test(digits)) return "dinersclub";
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Format card number with spaces: 4111111111111111 → 4111 1111 1111 1111
|
|
76
|
+
*/
|
|
77
|
+
export function formatCardNumber(cardNumber: string): string {
|
|
78
|
+
const digits = cardNumber.replace(/\D/g, "");
|
|
79
|
+
if (detectCardNetwork(digits) === "amex") {
|
|
80
|
+
return digits.replace(/(\d{4})(\d{6})(\d{5})/, "$1 $2 $3");
|
|
81
|
+
}
|
|
82
|
+
return digits.replace(/(\d{4})/g, "$1 ").trim();
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Validate card number using Luhn algorithm
|
|
87
|
+
*/
|
|
88
|
+
export function validateCardNumber(cardNumber: string): boolean {
|
|
89
|
+
const digits = cardNumber.replace(/\D/g, "");
|
|
90
|
+
if (digits.length < 13 || digits.length > 19) return false;
|
|
91
|
+
|
|
92
|
+
let sum = 0;
|
|
93
|
+
let isEven = false;
|
|
94
|
+
for (let i = digits.length - 1; i >= 0; i--) {
|
|
95
|
+
let d = parseInt(digits[i], 10);
|
|
96
|
+
if (isEven) {
|
|
97
|
+
d *= 2;
|
|
98
|
+
if (d > 9) d -= 9;
|
|
99
|
+
}
|
|
100
|
+
sum += d;
|
|
101
|
+
isEven = !isEven;
|
|
102
|
+
}
|
|
103
|
+
return sum % 10 === 0;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Calculate subscription renewal dates
|
|
108
|
+
*/
|
|
109
|
+
export function getNextBillingDate(
|
|
110
|
+
plan: SubscriptionPlan,
|
|
111
|
+
currentDate: Date = new Date(),
|
|
112
|
+
): Date {
|
|
113
|
+
const next = new Date(currentDate);
|
|
114
|
+
switch (plan.interval) {
|
|
115
|
+
case "daily":
|
|
116
|
+
next.setDate(next.getDate() + plan.intervalCount);
|
|
117
|
+
break;
|
|
118
|
+
case "weekly":
|
|
119
|
+
next.setDate(next.getDate() + 7 * plan.intervalCount);
|
|
120
|
+
break;
|
|
121
|
+
case "monthly":
|
|
122
|
+
next.setMonth(next.getMonth() + plan.intervalCount);
|
|
123
|
+
break;
|
|
124
|
+
case "quarterly":
|
|
125
|
+
next.setMonth(next.getMonth() + 3 * plan.intervalCount);
|
|
126
|
+
break;
|
|
127
|
+
case "yearly":
|
|
128
|
+
next.setFullYear(next.getFullYear() + plan.intervalCount);
|
|
129
|
+
break;
|
|
130
|
+
}
|
|
131
|
+
return next;
|
|
132
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @rajeev02/payments
|
|
3
|
+
* Payments Abstraction SDK — India focused
|
|
4
|
+
* UPI, wallets, cards, subscriptions
|
|
5
|
+
*
|
|
6
|
+
* @author Rajeev Kumar Joshi
|
|
7
|
+
* @license MIT
|
|
8
|
+
*/
|
|
9
|
+
export {
|
|
10
|
+
generateUpiUri,
|
|
11
|
+
generateUpiQrData,
|
|
12
|
+
validateVpa,
|
|
13
|
+
getPspName,
|
|
14
|
+
UPI_PSP_HANDLES,
|
|
15
|
+
} from "./upi";
|
|
16
|
+
export type {
|
|
17
|
+
UpiConfig,
|
|
18
|
+
UpiPaymentRequest,
|
|
19
|
+
UpiPaymentResult,
|
|
20
|
+
UpiMandateRequest,
|
|
21
|
+
} from "./upi";
|
|
22
|
+
|
|
23
|
+
export { WalletManager } from "./wallet";
|
|
24
|
+
export type {
|
|
25
|
+
WalletProvider,
|
|
26
|
+
WalletConfig,
|
|
27
|
+
WalletPaymentRequest,
|
|
28
|
+
WalletPaymentResult,
|
|
29
|
+
} from "./wallet";
|
|
30
|
+
|
|
31
|
+
export {
|
|
32
|
+
detectCardNetwork,
|
|
33
|
+
formatCardNumber,
|
|
34
|
+
validateCardNumber,
|
|
35
|
+
getNextBillingDate,
|
|
36
|
+
} from "./cards";
|
|
37
|
+
export type { CardInfo, SubscriptionPlan, Subscription } from "./cards";
|
package/src/upi/index.ts
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @rajeev02/payments — UPI Module
|
|
3
|
+
* UPI intent, collect request, QR code, autopay mandates
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export interface UpiConfig {
|
|
7
|
+
/** Merchant VPA (e.g., merchant@paytm) */
|
|
8
|
+
merchantVpa: string;
|
|
9
|
+
/** Merchant name shown in UPI apps */
|
|
10
|
+
merchantName: string;
|
|
11
|
+
/** Merchant category code */
|
|
12
|
+
mcc?: string;
|
|
13
|
+
/** Transaction reference prefix */
|
|
14
|
+
txnRefPrefix?: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface UpiPaymentRequest {
|
|
18
|
+
/** Amount in INR (paisa precision) */
|
|
19
|
+
amount: number;
|
|
20
|
+
/** Payment description */
|
|
21
|
+
note: string;
|
|
22
|
+
/** Unique transaction reference */
|
|
23
|
+
txnRef?: string;
|
|
24
|
+
/** Customer VPA (for collect requests) */
|
|
25
|
+
customerVpa?: string;
|
|
26
|
+
/** Order ID from your backend */
|
|
27
|
+
orderId: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface UpiPaymentResult {
|
|
31
|
+
success: boolean;
|
|
32
|
+
txnId?: string;
|
|
33
|
+
txnRef?: string;
|
|
34
|
+
responseCode?: string;
|
|
35
|
+
approvalRefNo?: string;
|
|
36
|
+
status: "success" | "failed" | "pending" | "cancelled";
|
|
37
|
+
error?: string;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface UpiMandateRequest {
|
|
41
|
+
/** Customer VPA */
|
|
42
|
+
customerVpa: string;
|
|
43
|
+
/** Maximum amount per debit */
|
|
44
|
+
maxAmount: number;
|
|
45
|
+
/** Frequency: DAILY, WEEKLY, FORTNIGHTLY, MONTHLY, BIMONTHLY, QUARTERLY, HALFYEARLY, YEARLY */
|
|
46
|
+
frequency: string;
|
|
47
|
+
/** Start date (ISO) */
|
|
48
|
+
startDate: string;
|
|
49
|
+
/** End date (ISO) */
|
|
50
|
+
endDate: string;
|
|
51
|
+
/** Purpose description */
|
|
52
|
+
purpose: string;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Generate UPI deep link URI for intent-based payment
|
|
57
|
+
* Works with all UPI apps (GPay, PhonePe, Paytm, etc.)
|
|
58
|
+
*/
|
|
59
|
+
export function generateUpiUri(
|
|
60
|
+
config: UpiConfig,
|
|
61
|
+
request: UpiPaymentRequest,
|
|
62
|
+
): string {
|
|
63
|
+
const txnRef =
|
|
64
|
+
request.txnRef || `${config.txnRefPrefix || "TXN"}${Date.now()}`;
|
|
65
|
+
const params = new URLSearchParams({
|
|
66
|
+
pa: config.merchantVpa,
|
|
67
|
+
pn: config.merchantName,
|
|
68
|
+
am: request.amount.toFixed(2),
|
|
69
|
+
cu: "INR",
|
|
70
|
+
tn: request.note,
|
|
71
|
+
tr: txnRef,
|
|
72
|
+
});
|
|
73
|
+
if (config.mcc) params.set("mc", config.mcc);
|
|
74
|
+
if (request.orderId) params.set("tid", request.orderId);
|
|
75
|
+
return `upi://pay?${params.toString()}`;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Generate UPI QR code data string
|
|
80
|
+
*/
|
|
81
|
+
export function generateUpiQrData(
|
|
82
|
+
config: UpiConfig,
|
|
83
|
+
request: UpiPaymentRequest,
|
|
84
|
+
): string {
|
|
85
|
+
return generateUpiUri(config, request);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Validate a UPI VPA format
|
|
90
|
+
* Format: username@psp (e.g., rajeev@paytm, user@okicici)
|
|
91
|
+
*/
|
|
92
|
+
export function validateVpa(vpa: string): boolean {
|
|
93
|
+
return /^[a-zA-Z0-9._-]+@[a-zA-Z0-9]+$/.test(vpa);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Known UPI PSP handles
|
|
98
|
+
*/
|
|
99
|
+
export const UPI_PSP_HANDLES: Record<string, string> = {
|
|
100
|
+
"@paytm": "Paytm",
|
|
101
|
+
"@okicici": "Google Pay (ICICI)",
|
|
102
|
+
"@okhdfcbank": "Google Pay (HDFC)",
|
|
103
|
+
"@oksbi": "Google Pay (SBI)",
|
|
104
|
+
"@ybl": "PhonePe (YES Bank)",
|
|
105
|
+
"@ibl": "PhonePe (ICICI)",
|
|
106
|
+
"@axl": "PhonePe (Axis)",
|
|
107
|
+
"@apl": "Amazon Pay",
|
|
108
|
+
"@freecharge": "Freecharge",
|
|
109
|
+
"@upi": "BHIM",
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
export function getPspName(vpa: string): string | null {
|
|
113
|
+
const handle = "@" + vpa.split("@")[1];
|
|
114
|
+
return UPI_PSP_HANDLES[handle] ?? null;
|
|
115
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @rajeev02/payments — Wallet Module
|
|
3
|
+
* Abstraction over PhonePe, Paytm, Amazon Pay, Freecharge wallets
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export type WalletProvider =
|
|
7
|
+
| "paytm"
|
|
8
|
+
| "phonepe"
|
|
9
|
+
| "amazon_pay"
|
|
10
|
+
| "freecharge"
|
|
11
|
+
| "mobikwik"
|
|
12
|
+
| "jio_pay";
|
|
13
|
+
|
|
14
|
+
export interface WalletConfig {
|
|
15
|
+
provider: WalletProvider;
|
|
16
|
+
merchantId: string;
|
|
17
|
+
merchantKey?: string;
|
|
18
|
+
environment: "sandbox" | "production";
|
|
19
|
+
callbackUrl: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface WalletPaymentRequest {
|
|
23
|
+
orderId: string;
|
|
24
|
+
amount: number;
|
|
25
|
+
customerId: string;
|
|
26
|
+
customerPhone?: string;
|
|
27
|
+
customerEmail?: string;
|
|
28
|
+
description?: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface WalletPaymentResult {
|
|
32
|
+
success: boolean;
|
|
33
|
+
provider: WalletProvider;
|
|
34
|
+
txnId?: string;
|
|
35
|
+
orderId: string;
|
|
36
|
+
status: "success" | "failed" | "pending" | "cancelled";
|
|
37
|
+
walletBalance?: number;
|
|
38
|
+
error?: string;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Wallet Manager — handles payment flow across wallet providers
|
|
43
|
+
*/
|
|
44
|
+
export class WalletManager {
|
|
45
|
+
private configs: Map<WalletProvider, WalletConfig> = new Map();
|
|
46
|
+
|
|
47
|
+
/** Register a wallet provider */
|
|
48
|
+
register(config: WalletConfig): void {
|
|
49
|
+
this.configs.set(config.provider, config);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** Get all registered wallet providers */
|
|
53
|
+
getAvailableWallets(): WalletProvider[] {
|
|
54
|
+
return Array.from(this.configs.keys());
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** Check if a wallet is registered */
|
|
58
|
+
isAvailable(provider: WalletProvider): boolean {
|
|
59
|
+
return this.configs.has(provider);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** Generate checkout URL/payload for a wallet */
|
|
63
|
+
generateCheckout(
|
|
64
|
+
provider: WalletProvider,
|
|
65
|
+
request: WalletPaymentRequest,
|
|
66
|
+
): Record<string, unknown> | null {
|
|
67
|
+
const config = this.configs.get(provider);
|
|
68
|
+
if (!config) return null;
|
|
69
|
+
|
|
70
|
+
return {
|
|
71
|
+
provider,
|
|
72
|
+
merchantId: config.merchantId,
|
|
73
|
+
environment: config.environment,
|
|
74
|
+
callbackUrl: config.callbackUrl,
|
|
75
|
+
orderId: request.orderId,
|
|
76
|
+
amount: request.amount,
|
|
77
|
+
customerId: request.customerId,
|
|
78
|
+
phone: request.customerPhone,
|
|
79
|
+
email: request.customerEmail,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
}
|