@dev_innovations_labs/phonepe-pg-sdk 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/README.md +0 -0
- package/dist/PhonePeNode.d.ts +9 -0
- package/dist/PhonePeReact.d.ts +14 -0
- package/dist/PhonePeReactNative.d.ts +9 -0
- package/dist/env.d.ts +14 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.esm.js +139 -0
- package/dist/index.js +142 -0
- package/dist/native.d.ts +1 -0
- package/dist/native.esm.js +81 -0
- package/dist/native.js +83 -0
- package/dist/types.d.ts +15 -0
- package/dist/utils.d.ts +2 -0
- package/package.json +72 -0
- package/rollup.config.js +26 -0
- package/src/PhonePeNode.ts +74 -0
- package/src/PhonePeReact.tsx +89 -0
- package/src/PhonePeReactNative.tsx +81 -0
- package/src/env.ts +15 -0
- package/src/index.ts +3 -0
- package/src/native.ts +1 -0
- package/src/types/react-native-webview.d.ts +4 -0
- package/src/types.ts +17 -0
- package/src/utils.ts +12 -0
- package/tsconfig.json +14 -0
package/README.md
ADDED
|
File without changes
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { PhonePeCredentials, PhonePePaymentInit } from "./types";
|
|
2
|
+
export declare class PhonePeNode {
|
|
3
|
+
private creds;
|
|
4
|
+
constructor(creds: PhonePeCredentials);
|
|
5
|
+
private get urls();
|
|
6
|
+
getToken(): Promise<any>;
|
|
7
|
+
createPayment(data: PhonePePaymentInit): Promise<any>;
|
|
8
|
+
getStatus(merchantId: string, merchantOrderId: string): Promise<any>;
|
|
9
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { PhonePeCredentials, PhonePePaymentInit } from "./types";
|
|
3
|
+
declare global {
|
|
4
|
+
interface Window {
|
|
5
|
+
PhonePeCheckout: any;
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
interface Props extends PhonePeCredentials {
|
|
9
|
+
request: PhonePePaymentInit;
|
|
10
|
+
onSuccess: (data: any) => void;
|
|
11
|
+
onError: (err: any) => void;
|
|
12
|
+
}
|
|
13
|
+
export declare const PhonePeCheckoutWeb: React.FC<Props>;
|
|
14
|
+
export {};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { PhonePeCredentials, PhonePePaymentInit } from "./types";
|
|
3
|
+
interface Props extends PhonePeCredentials {
|
|
4
|
+
request: PhonePePaymentInit;
|
|
5
|
+
onSuccess: (data: any) => void;
|
|
6
|
+
onError: (err: any) => void;
|
|
7
|
+
}
|
|
8
|
+
export declare const PhonePeCheckoutNative: React.FC<Props>;
|
|
9
|
+
export {};
|
package/dist/env.d.ts
ADDED
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import axios from 'axios';
|
|
2
|
+
import { v4 } from 'uuid';
|
|
3
|
+
import { useEffect } from 'react';
|
|
4
|
+
|
|
5
|
+
const PhonePeUrls = {
|
|
6
|
+
production: {
|
|
7
|
+
token: "https://api.phonepe.com/apis/identity-manager/v1/oauth/token",
|
|
8
|
+
pay: "https://api.phonepe.com/apis/pg/checkout/v2/pay",
|
|
9
|
+
status: "https://api.phonepe.com/apis/pg/checkout/v2/order/status",
|
|
10
|
+
script: "https://mercury.phonepe.com/web/bundle/checkout.js",
|
|
11
|
+
},
|
|
12
|
+
sandbox: {
|
|
13
|
+
token: "https://api-preprod.phonepe.com/apis/identity-manager/v1/oauth/token",
|
|
14
|
+
pay: "https://api-preprod.phonepe.com/apis/pg-sandbox/checkout/v2/pay",
|
|
15
|
+
status: "https://api-preprod.phonepe.com/apis/pg-sandbox/checkout/v2/order/status",
|
|
16
|
+
script: "https://mercury.phonepe.com/web/bundle/checkout.js",
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
function normalizeEnvironment(env) {
|
|
21
|
+
const e = String(env).toLowerCase().trim();
|
|
22
|
+
if (e === "production" || e === "prod")
|
|
23
|
+
return "production";
|
|
24
|
+
if (e === "sandbox")
|
|
25
|
+
return "sandbox";
|
|
26
|
+
throw new Error(`Invalid environment "${env}". Expected "Production" or "Sandbox".`);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
class PhonePeNode {
|
|
30
|
+
constructor(creds) {
|
|
31
|
+
this.creds = creds;
|
|
32
|
+
}
|
|
33
|
+
get urls() {
|
|
34
|
+
const env = normalizeEnvironment(this.creds.environment);
|
|
35
|
+
return PhonePeUrls[env];
|
|
36
|
+
}
|
|
37
|
+
async getToken() {
|
|
38
|
+
const body = new URLSearchParams({
|
|
39
|
+
client_id: this.creds.clientId,
|
|
40
|
+
client_secret: this.creds.clientSecret,
|
|
41
|
+
grant_type: "client_credentials",
|
|
42
|
+
client_version: "1"
|
|
43
|
+
});
|
|
44
|
+
const res = await axios.post(this.urls.token, body, {
|
|
45
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
46
|
+
});
|
|
47
|
+
return res.data.access_token;
|
|
48
|
+
}
|
|
49
|
+
async createPayment(data) {
|
|
50
|
+
const token = await this.getToken();
|
|
51
|
+
const payload = {
|
|
52
|
+
merchantId: data.merchantId,
|
|
53
|
+
merchantOrderId: v4(),
|
|
54
|
+
merchantTransactionId: v4(),
|
|
55
|
+
amount: data.amount,
|
|
56
|
+
mobileNumber: data.mobileNumber,
|
|
57
|
+
redirectUrl: data.redirectUrl,
|
|
58
|
+
callbackUrl: data.callbackUrl,
|
|
59
|
+
deviceContext: data.deviceContext,
|
|
60
|
+
paymentScope: data.paymentScope,
|
|
61
|
+
};
|
|
62
|
+
const res = await axios.post(this.urls.pay, payload, {
|
|
63
|
+
headers: {
|
|
64
|
+
"Content-Type": "application/json",
|
|
65
|
+
Authorization: `O-Bearer ${token}`,
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
return res.data;
|
|
69
|
+
}
|
|
70
|
+
async getStatus(merchantId, merchantOrderId) {
|
|
71
|
+
const token = await this.getToken();
|
|
72
|
+
const res = await axios.post(this.urls.status, {
|
|
73
|
+
merchantId,
|
|
74
|
+
merchantOrderId
|
|
75
|
+
}, {
|
|
76
|
+
headers: {
|
|
77
|
+
"Content-Type": "application/json",
|
|
78
|
+
Authorization: `O-Bearer ${token}`,
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
return res.data;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const PhonePeCheckoutWeb = ({ environment, clientId, clientSecret, request, onSuccess, onError }) => {
|
|
86
|
+
const env = normalizeEnvironment(environment);
|
|
87
|
+
const urls = PhonePeUrls[env];
|
|
88
|
+
if (!urls) {
|
|
89
|
+
onError(`Invalid environment: ${environment}`);
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
useEffect(() => {
|
|
93
|
+
const script = document.createElement("script");
|
|
94
|
+
script.src = urls.script;
|
|
95
|
+
script.async = true;
|
|
96
|
+
document.body.appendChild(script);
|
|
97
|
+
script.onload = () => init();
|
|
98
|
+
}, []);
|
|
99
|
+
const init = async () => {
|
|
100
|
+
try {
|
|
101
|
+
const tokenResp = await fetch(urls.token, {
|
|
102
|
+
method: "POST",
|
|
103
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
104
|
+
body: new URLSearchParams({
|
|
105
|
+
client_id: clientId,
|
|
106
|
+
client_secret: clientSecret,
|
|
107
|
+
grant_type: "client_credentials",
|
|
108
|
+
client_version: "1"
|
|
109
|
+
})
|
|
110
|
+
});
|
|
111
|
+
const token = (await tokenResp.json()).access_token;
|
|
112
|
+
const payload = {
|
|
113
|
+
...request,
|
|
114
|
+
merchantOrderId: v4(),
|
|
115
|
+
merchantTransactionId: v4()
|
|
116
|
+
};
|
|
117
|
+
const payResp = await fetch(urls.pay, {
|
|
118
|
+
method: "POST",
|
|
119
|
+
headers: {
|
|
120
|
+
"Content-Type": "application/json",
|
|
121
|
+
Authorization: `O-Bearer ${token}`
|
|
122
|
+
},
|
|
123
|
+
body: JSON.stringify(payload)
|
|
124
|
+
});
|
|
125
|
+
const payData = await payResp.json();
|
|
126
|
+
window.PhonePeCheckout.transact({
|
|
127
|
+
tokenUrl: payData.redirectUrl,
|
|
128
|
+
type: "IFRAME",
|
|
129
|
+
callback: () => onSuccess(payData)
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
catch (err) {
|
|
133
|
+
onError(err);
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
return null;
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
export { PhonePeCheckoutWeb, PhonePeNode };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var axios = require('axios');
|
|
4
|
+
var uuid = require('uuid');
|
|
5
|
+
var react = require('react');
|
|
6
|
+
|
|
7
|
+
const PhonePeUrls = {
|
|
8
|
+
production: {
|
|
9
|
+
token: "https://api.phonepe.com/apis/identity-manager/v1/oauth/token",
|
|
10
|
+
pay: "https://api.phonepe.com/apis/pg/checkout/v2/pay",
|
|
11
|
+
status: "https://api.phonepe.com/apis/pg/checkout/v2/order/status",
|
|
12
|
+
script: "https://mercury.phonepe.com/web/bundle/checkout.js",
|
|
13
|
+
},
|
|
14
|
+
sandbox: {
|
|
15
|
+
token: "https://api-preprod.phonepe.com/apis/identity-manager/v1/oauth/token",
|
|
16
|
+
pay: "https://api-preprod.phonepe.com/apis/pg-sandbox/checkout/v2/pay",
|
|
17
|
+
status: "https://api-preprod.phonepe.com/apis/pg-sandbox/checkout/v2/order/status",
|
|
18
|
+
script: "https://mercury.phonepe.com/web/bundle/checkout.js",
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
function normalizeEnvironment(env) {
|
|
23
|
+
const e = String(env).toLowerCase().trim();
|
|
24
|
+
if (e === "production" || e === "prod")
|
|
25
|
+
return "production";
|
|
26
|
+
if (e === "sandbox")
|
|
27
|
+
return "sandbox";
|
|
28
|
+
throw new Error(`Invalid environment "${env}". Expected "Production" or "Sandbox".`);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
class PhonePeNode {
|
|
32
|
+
constructor(creds) {
|
|
33
|
+
this.creds = creds;
|
|
34
|
+
}
|
|
35
|
+
get urls() {
|
|
36
|
+
const env = normalizeEnvironment(this.creds.environment);
|
|
37
|
+
return PhonePeUrls[env];
|
|
38
|
+
}
|
|
39
|
+
async getToken() {
|
|
40
|
+
const body = new URLSearchParams({
|
|
41
|
+
client_id: this.creds.clientId,
|
|
42
|
+
client_secret: this.creds.clientSecret,
|
|
43
|
+
grant_type: "client_credentials",
|
|
44
|
+
client_version: "1"
|
|
45
|
+
});
|
|
46
|
+
const res = await axios.post(this.urls.token, body, {
|
|
47
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
48
|
+
});
|
|
49
|
+
return res.data.access_token;
|
|
50
|
+
}
|
|
51
|
+
async createPayment(data) {
|
|
52
|
+
const token = await this.getToken();
|
|
53
|
+
const payload = {
|
|
54
|
+
merchantId: data.merchantId,
|
|
55
|
+
merchantOrderId: uuid.v4(),
|
|
56
|
+
merchantTransactionId: uuid.v4(),
|
|
57
|
+
amount: data.amount,
|
|
58
|
+
mobileNumber: data.mobileNumber,
|
|
59
|
+
redirectUrl: data.redirectUrl,
|
|
60
|
+
callbackUrl: data.callbackUrl,
|
|
61
|
+
deviceContext: data.deviceContext,
|
|
62
|
+
paymentScope: data.paymentScope,
|
|
63
|
+
};
|
|
64
|
+
const res = await axios.post(this.urls.pay, payload, {
|
|
65
|
+
headers: {
|
|
66
|
+
"Content-Type": "application/json",
|
|
67
|
+
Authorization: `O-Bearer ${token}`,
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
return res.data;
|
|
71
|
+
}
|
|
72
|
+
async getStatus(merchantId, merchantOrderId) {
|
|
73
|
+
const token = await this.getToken();
|
|
74
|
+
const res = await axios.post(this.urls.status, {
|
|
75
|
+
merchantId,
|
|
76
|
+
merchantOrderId
|
|
77
|
+
}, {
|
|
78
|
+
headers: {
|
|
79
|
+
"Content-Type": "application/json",
|
|
80
|
+
Authorization: `O-Bearer ${token}`,
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
return res.data;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const PhonePeCheckoutWeb = ({ environment, clientId, clientSecret, request, onSuccess, onError }) => {
|
|
88
|
+
const env = normalizeEnvironment(environment);
|
|
89
|
+
const urls = PhonePeUrls[env];
|
|
90
|
+
if (!urls) {
|
|
91
|
+
onError(`Invalid environment: ${environment}`);
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
react.useEffect(() => {
|
|
95
|
+
const script = document.createElement("script");
|
|
96
|
+
script.src = urls.script;
|
|
97
|
+
script.async = true;
|
|
98
|
+
document.body.appendChild(script);
|
|
99
|
+
script.onload = () => init();
|
|
100
|
+
}, []);
|
|
101
|
+
const init = async () => {
|
|
102
|
+
try {
|
|
103
|
+
const tokenResp = await fetch(urls.token, {
|
|
104
|
+
method: "POST",
|
|
105
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
106
|
+
body: new URLSearchParams({
|
|
107
|
+
client_id: clientId,
|
|
108
|
+
client_secret: clientSecret,
|
|
109
|
+
grant_type: "client_credentials",
|
|
110
|
+
client_version: "1"
|
|
111
|
+
})
|
|
112
|
+
});
|
|
113
|
+
const token = (await tokenResp.json()).access_token;
|
|
114
|
+
const payload = {
|
|
115
|
+
...request,
|
|
116
|
+
merchantOrderId: uuid.v4(),
|
|
117
|
+
merchantTransactionId: uuid.v4()
|
|
118
|
+
};
|
|
119
|
+
const payResp = await fetch(urls.pay, {
|
|
120
|
+
method: "POST",
|
|
121
|
+
headers: {
|
|
122
|
+
"Content-Type": "application/json",
|
|
123
|
+
Authorization: `O-Bearer ${token}`
|
|
124
|
+
},
|
|
125
|
+
body: JSON.stringify(payload)
|
|
126
|
+
});
|
|
127
|
+
const payData = await payResp.json();
|
|
128
|
+
window.PhonePeCheckout.transact({
|
|
129
|
+
tokenUrl: payData.redirectUrl,
|
|
130
|
+
type: "IFRAME",
|
|
131
|
+
callback: () => onSuccess(payData)
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
catch (err) {
|
|
135
|
+
onError(err);
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
return null;
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
exports.PhonePeCheckoutWeb = PhonePeCheckoutWeb;
|
|
142
|
+
exports.PhonePeNode = PhonePeNode;
|
package/dist/native.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./PhonePeReactNative";
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { jsx } from 'react/jsx-runtime';
|
|
2
|
+
import { useState, useEffect } from 'react';
|
|
3
|
+
import { WebView } from 'react-native-webview';
|
|
4
|
+
import { v4 } from 'uuid';
|
|
5
|
+
|
|
6
|
+
const PhonePeUrls = {
|
|
7
|
+
production: {
|
|
8
|
+
token: "https://api.phonepe.com/apis/identity-manager/v1/oauth/token",
|
|
9
|
+
pay: "https://api.phonepe.com/apis/pg/checkout/v2/pay",
|
|
10
|
+
status: "https://api.phonepe.com/apis/pg/checkout/v2/order/status",
|
|
11
|
+
script: "https://mercury.phonepe.com/web/bundle/checkout.js",
|
|
12
|
+
},
|
|
13
|
+
sandbox: {
|
|
14
|
+
token: "https://api-preprod.phonepe.com/apis/identity-manager/v1/oauth/token",
|
|
15
|
+
pay: "https://api-preprod.phonepe.com/apis/pg-sandbox/checkout/v2/pay",
|
|
16
|
+
status: "https://api-preprod.phonepe.com/apis/pg-sandbox/checkout/v2/order/status",
|
|
17
|
+
script: "https://mercury.phonepe.com/web/bundle/checkout.js",
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
function normalizeEnvironment(env) {
|
|
22
|
+
const e = String(env).toLowerCase().trim();
|
|
23
|
+
if (e === "production" || e === "prod")
|
|
24
|
+
return "production";
|
|
25
|
+
if (e === "sandbox")
|
|
26
|
+
return "sandbox";
|
|
27
|
+
throw new Error(`Invalid environment "${env}". Expected "Production" or "Sandbox".`);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const PhonePeCheckoutNative = ({ clientId, clientSecret, environment, request, onSuccess, onError }) => {
|
|
31
|
+
const [url, setUrl] = useState();
|
|
32
|
+
const env = normalizeEnvironment(environment);
|
|
33
|
+
const urls = PhonePeUrls[env];
|
|
34
|
+
if (!urls) {
|
|
35
|
+
onError(`Invalid environment: ${environment}`);
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
useEffect(() => {
|
|
39
|
+
init();
|
|
40
|
+
}, []);
|
|
41
|
+
const init = async () => {
|
|
42
|
+
try {
|
|
43
|
+
const tokenResp = await fetch(urls.token, {
|
|
44
|
+
method: "POST",
|
|
45
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
46
|
+
body: new URLSearchParams({
|
|
47
|
+
client_id: clientId,
|
|
48
|
+
client_secret: clientSecret,
|
|
49
|
+
grant_type: "client_credentials",
|
|
50
|
+
client_version: "1"
|
|
51
|
+
})
|
|
52
|
+
});
|
|
53
|
+
const token = (await tokenResp.json()).access_token;
|
|
54
|
+
const payload = {
|
|
55
|
+
...request,
|
|
56
|
+
merchantOrderId: v4(),
|
|
57
|
+
merchantTransactionId: v4()
|
|
58
|
+
};
|
|
59
|
+
const payResp = await fetch(urls.pay, {
|
|
60
|
+
method: "POST",
|
|
61
|
+
headers: {
|
|
62
|
+
"Content-Type": "application/json",
|
|
63
|
+
Authorization: `O-Bearer ${token}`
|
|
64
|
+
},
|
|
65
|
+
body: JSON.stringify(payload)
|
|
66
|
+
});
|
|
67
|
+
const { redirectUrl } = await payResp.json();
|
|
68
|
+
setUrl(redirectUrl);
|
|
69
|
+
}
|
|
70
|
+
catch (err) {
|
|
71
|
+
onError(err);
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
return url ? (jsx(WebView, { source: { uri: url }, onNavigationStateChange: (nav) => {
|
|
75
|
+
if (nav.url.includes("callback")) {
|
|
76
|
+
onSuccess(nav);
|
|
77
|
+
}
|
|
78
|
+
} })) : null;
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
export { PhonePeCheckoutNative };
|
package/dist/native.js
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
4
|
+
var react = require('react');
|
|
5
|
+
var reactNativeWebview = require('react-native-webview');
|
|
6
|
+
var uuid = require('uuid');
|
|
7
|
+
|
|
8
|
+
const PhonePeUrls = {
|
|
9
|
+
production: {
|
|
10
|
+
token: "https://api.phonepe.com/apis/identity-manager/v1/oauth/token",
|
|
11
|
+
pay: "https://api.phonepe.com/apis/pg/checkout/v2/pay",
|
|
12
|
+
status: "https://api.phonepe.com/apis/pg/checkout/v2/order/status",
|
|
13
|
+
script: "https://mercury.phonepe.com/web/bundle/checkout.js",
|
|
14
|
+
},
|
|
15
|
+
sandbox: {
|
|
16
|
+
token: "https://api-preprod.phonepe.com/apis/identity-manager/v1/oauth/token",
|
|
17
|
+
pay: "https://api-preprod.phonepe.com/apis/pg-sandbox/checkout/v2/pay",
|
|
18
|
+
status: "https://api-preprod.phonepe.com/apis/pg-sandbox/checkout/v2/order/status",
|
|
19
|
+
script: "https://mercury.phonepe.com/web/bundle/checkout.js",
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
function normalizeEnvironment(env) {
|
|
24
|
+
const e = String(env).toLowerCase().trim();
|
|
25
|
+
if (e === "production" || e === "prod")
|
|
26
|
+
return "production";
|
|
27
|
+
if (e === "sandbox")
|
|
28
|
+
return "sandbox";
|
|
29
|
+
throw new Error(`Invalid environment "${env}". Expected "Production" or "Sandbox".`);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const PhonePeCheckoutNative = ({ clientId, clientSecret, environment, request, onSuccess, onError }) => {
|
|
33
|
+
const [url, setUrl] = react.useState();
|
|
34
|
+
const env = normalizeEnvironment(environment);
|
|
35
|
+
const urls = PhonePeUrls[env];
|
|
36
|
+
if (!urls) {
|
|
37
|
+
onError(`Invalid environment: ${environment}`);
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
react.useEffect(() => {
|
|
41
|
+
init();
|
|
42
|
+
}, []);
|
|
43
|
+
const init = async () => {
|
|
44
|
+
try {
|
|
45
|
+
const tokenResp = await fetch(urls.token, {
|
|
46
|
+
method: "POST",
|
|
47
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
48
|
+
body: new URLSearchParams({
|
|
49
|
+
client_id: clientId,
|
|
50
|
+
client_secret: clientSecret,
|
|
51
|
+
grant_type: "client_credentials",
|
|
52
|
+
client_version: "1"
|
|
53
|
+
})
|
|
54
|
+
});
|
|
55
|
+
const token = (await tokenResp.json()).access_token;
|
|
56
|
+
const payload = {
|
|
57
|
+
...request,
|
|
58
|
+
merchantOrderId: uuid.v4(),
|
|
59
|
+
merchantTransactionId: uuid.v4()
|
|
60
|
+
};
|
|
61
|
+
const payResp = await fetch(urls.pay, {
|
|
62
|
+
method: "POST",
|
|
63
|
+
headers: {
|
|
64
|
+
"Content-Type": "application/json",
|
|
65
|
+
Authorization: `O-Bearer ${token}`
|
|
66
|
+
},
|
|
67
|
+
body: JSON.stringify(payload)
|
|
68
|
+
});
|
|
69
|
+
const { redirectUrl } = await payResp.json();
|
|
70
|
+
setUrl(redirectUrl);
|
|
71
|
+
}
|
|
72
|
+
catch (err) {
|
|
73
|
+
onError(err);
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
return url ? (jsxRuntime.jsx(reactNativeWebview.WebView, { source: { uri: url }, onNavigationStateChange: (nav) => {
|
|
77
|
+
if (nav.url.includes("callback")) {
|
|
78
|
+
onSuccess(nav);
|
|
79
|
+
}
|
|
80
|
+
} })) : null;
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
exports.PhonePeCheckoutNative = PhonePeCheckoutNative;
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export type PhonePeEnvironment = "Production" | "Sandbox";
|
|
2
|
+
export interface PhonePeCredentials {
|
|
3
|
+
clientId: string;
|
|
4
|
+
clientSecret: string;
|
|
5
|
+
environment: PhonePeEnvironment;
|
|
6
|
+
}
|
|
7
|
+
export interface PhonePePaymentInit {
|
|
8
|
+
merchantId: string;
|
|
9
|
+
amount: number;
|
|
10
|
+
mobileNumber?: string;
|
|
11
|
+
redirectUrl: string;
|
|
12
|
+
callbackUrl: string;
|
|
13
|
+
deviceContext?: any;
|
|
14
|
+
paymentScope?: string;
|
|
15
|
+
}
|
package/dist/utils.d.ts
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@dev_innovations_labs/phonepe-pg-sdk",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
|
|
6
|
+
"keywords": [
|
|
7
|
+
"phonepe",
|
|
8
|
+
"phonepe-pg",
|
|
9
|
+
"payment-gateway",
|
|
10
|
+
"pg-sdk",
|
|
11
|
+
"checkout-v2",
|
|
12
|
+
"upi",
|
|
13
|
+
"react",
|
|
14
|
+
"react-native",
|
|
15
|
+
"nodejs",
|
|
16
|
+
"javascript",
|
|
17
|
+
"typescript",
|
|
18
|
+
"web-payments"
|
|
19
|
+
],
|
|
20
|
+
|
|
21
|
+
"main": "dist/index.js",
|
|
22
|
+
"module": "dist/index.esm.js",
|
|
23
|
+
"types": "dist/index.d.ts",
|
|
24
|
+
|
|
25
|
+
"author": {
|
|
26
|
+
"name": "Dev Innovations Labs"
|
|
27
|
+
},
|
|
28
|
+
|
|
29
|
+
"exports": {
|
|
30
|
+
".": {
|
|
31
|
+
"import": "./dist/index.esm.js",
|
|
32
|
+
"require": "./dist/index.js",
|
|
33
|
+
"types": "./dist/index.d.ts"
|
|
34
|
+
},
|
|
35
|
+
"./native": {
|
|
36
|
+
"import": "./dist/native.esm.js",
|
|
37
|
+
"require": "./dist/native.js",
|
|
38
|
+
"types": "./dist/native.d.ts"
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
|
|
42
|
+
"scripts": {
|
|
43
|
+
"build": "rollup -c"
|
|
44
|
+
},
|
|
45
|
+
|
|
46
|
+
"dependencies": {
|
|
47
|
+
"axios": "^1.6.0",
|
|
48
|
+
"uuid": "^9.0.0"
|
|
49
|
+
},
|
|
50
|
+
|
|
51
|
+
"peerDependencies": {
|
|
52
|
+
"react": "*",
|
|
53
|
+
"react-native": "*",
|
|
54
|
+
"react-native-webview": "*"
|
|
55
|
+
},
|
|
56
|
+
|
|
57
|
+
"peerDependenciesMeta": {
|
|
58
|
+
"react-native-webview": {
|
|
59
|
+
"optional": true
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
|
|
63
|
+
"devDependencies": {
|
|
64
|
+
"typescript": "^5.3.3",
|
|
65
|
+
"rollup": "^4.9.0",
|
|
66
|
+
"rollup-plugin-typescript2": "^0.34.1"
|
|
67
|
+
},
|
|
68
|
+
|
|
69
|
+
"publishConfig": {
|
|
70
|
+
"access": "public"
|
|
71
|
+
}
|
|
72
|
+
}
|
package/rollup.config.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import typescript from "rollup-plugin-typescript2";
|
|
2
|
+
|
|
3
|
+
export default [
|
|
4
|
+
{
|
|
5
|
+
input: "src/index.ts",
|
|
6
|
+
output: [
|
|
7
|
+
{ file: "dist/index.js", format: "cjs" },
|
|
8
|
+
{ file: "dist/index.esm.js", format: "esm" }
|
|
9
|
+
],
|
|
10
|
+
plugins: [typescript()],
|
|
11
|
+
external: ["axios", "uuid", "react", "react/jsx-runtime", "react-native", "react-native-webview"]
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
input: "src/native.ts",
|
|
15
|
+
output: [
|
|
16
|
+
{ file: "dist/native.js", format: "cjs" },
|
|
17
|
+
{ file: "dist/native.esm.js", format: "esm" }
|
|
18
|
+
],
|
|
19
|
+
plugins: [typescript()],
|
|
20
|
+
external: [
|
|
21
|
+
"react",
|
|
22
|
+
"react-native",
|
|
23
|
+
"react-native-webview"
|
|
24
|
+
]
|
|
25
|
+
}
|
|
26
|
+
];
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import axios from "axios";
|
|
2
|
+
import { v4 as uuidv4 } from "uuid";
|
|
3
|
+
import { PhonePeCredentials, PhonePePaymentInit } from "./types";
|
|
4
|
+
import { PhonePeUrls } from "./env";
|
|
5
|
+
import { normalizeEnvironment } from "./utils";
|
|
6
|
+
|
|
7
|
+
export class PhonePeNode {
|
|
8
|
+
private creds: PhonePeCredentials;
|
|
9
|
+
|
|
10
|
+
constructor(creds: PhonePeCredentials) {
|
|
11
|
+
this.creds = creds;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
private get urls() {
|
|
15
|
+
const env = normalizeEnvironment(this.creds.environment);
|
|
16
|
+
return PhonePeUrls[env];
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
async getToken() {
|
|
20
|
+
const body = new URLSearchParams({
|
|
21
|
+
client_id: this.creds.clientId,
|
|
22
|
+
client_secret: this.creds.clientSecret,
|
|
23
|
+
grant_type: "client_credentials",
|
|
24
|
+
client_version: "1"
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
const res = await axios.post(this.urls.token, body, {
|
|
28
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
return res.data.access_token;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async createPayment(data: PhonePePaymentInit) {
|
|
35
|
+
const token = await this.getToken();
|
|
36
|
+
|
|
37
|
+
const payload = {
|
|
38
|
+
merchantId: data.merchantId,
|
|
39
|
+
merchantOrderId: uuidv4(),
|
|
40
|
+
merchantTransactionId: uuidv4(),
|
|
41
|
+
amount: data.amount,
|
|
42
|
+
mobileNumber: data.mobileNumber,
|
|
43
|
+
redirectUrl: data.redirectUrl,
|
|
44
|
+
callbackUrl: data.callbackUrl,
|
|
45
|
+
deviceContext: data.deviceContext,
|
|
46
|
+
paymentScope: data.paymentScope,
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const res = await axios.post(this.urls.pay, payload, {
|
|
50
|
+
headers: {
|
|
51
|
+
"Content-Type": "application/json",
|
|
52
|
+
Authorization: `O-Bearer ${token}`,
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
return res.data;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
async getStatus(merchantId: string, merchantOrderId: string) {
|
|
60
|
+
const token = await this.getToken();
|
|
61
|
+
|
|
62
|
+
const res = await axios.post(this.urls.status, {
|
|
63
|
+
merchantId,
|
|
64
|
+
merchantOrderId
|
|
65
|
+
}, {
|
|
66
|
+
headers: {
|
|
67
|
+
"Content-Type": "application/json",
|
|
68
|
+
Authorization: `O-Bearer ${token}`,
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
return res.data;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import React, { useEffect } from "react";
|
|
2
|
+
import { v4 as uuidv4 } from "uuid";
|
|
3
|
+
import { PhonePeCredentials, PhonePePaymentInit } from "./types";
|
|
4
|
+
import { PhonePeUrls } from "./env";
|
|
5
|
+
import { normalizeEnvironment } from "./utils";
|
|
6
|
+
|
|
7
|
+
declare global {
|
|
8
|
+
interface Window {
|
|
9
|
+
PhonePeCheckout: any;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
interface Props extends PhonePeCredentials {
|
|
14
|
+
request: PhonePePaymentInit;
|
|
15
|
+
onSuccess: (data: any) => void;
|
|
16
|
+
onError: (err: any) => void;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export const PhonePeCheckoutWeb: React.FC<Props> = ({
|
|
20
|
+
environment,
|
|
21
|
+
clientId,
|
|
22
|
+
clientSecret,
|
|
23
|
+
request,
|
|
24
|
+
onSuccess,
|
|
25
|
+
onError
|
|
26
|
+
}) => {
|
|
27
|
+
|
|
28
|
+
const env = normalizeEnvironment(environment);
|
|
29
|
+
const urls = PhonePeUrls[env];
|
|
30
|
+
|
|
31
|
+
if (!urls) {
|
|
32
|
+
onError(`Invalid environment: ${environment}`);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
useEffect(() => {
|
|
38
|
+
const script = document.createElement("script");
|
|
39
|
+
script.src = urls.script;
|
|
40
|
+
script.async = true;
|
|
41
|
+
document.body.appendChild(script);
|
|
42
|
+
|
|
43
|
+
script.onload = () => init();
|
|
44
|
+
}, []);
|
|
45
|
+
|
|
46
|
+
const init = async () => {
|
|
47
|
+
try {
|
|
48
|
+
const tokenResp = await fetch(urls.token, {
|
|
49
|
+
method: "POST",
|
|
50
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
51
|
+
body: new URLSearchParams({
|
|
52
|
+
client_id: clientId,
|
|
53
|
+
client_secret: clientSecret,
|
|
54
|
+
grant_type: "client_credentials",
|
|
55
|
+
client_version: "1"
|
|
56
|
+
})
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
const token = (await tokenResp.json()).access_token;
|
|
60
|
+
|
|
61
|
+
const payload = {
|
|
62
|
+
...request,
|
|
63
|
+
merchantOrderId: uuidv4(),
|
|
64
|
+
merchantTransactionId: uuidv4()
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
const payResp = await fetch(urls.pay, {
|
|
68
|
+
method: "POST",
|
|
69
|
+
headers: {
|
|
70
|
+
"Content-Type": "application/json",
|
|
71
|
+
Authorization: `O-Bearer ${token}`
|
|
72
|
+
},
|
|
73
|
+
body: JSON.stringify(payload)
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
const payData = await payResp.json();
|
|
77
|
+
|
|
78
|
+
window.PhonePeCheckout.transact({
|
|
79
|
+
tokenUrl: payData.redirectUrl,
|
|
80
|
+
type: "IFRAME",
|
|
81
|
+
callback: () => onSuccess(payData)
|
|
82
|
+
});
|
|
83
|
+
} catch (err) {
|
|
84
|
+
onError(err);
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
return null;
|
|
89
|
+
};
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import React, { useState, useEffect } from "react";
|
|
2
|
+
import { WebView } from "react-native-webview";
|
|
3
|
+
import { v4 } from "uuid";
|
|
4
|
+
import { PhonePeUrls } from "./env";
|
|
5
|
+
import { PhonePeCredentials, PhonePePaymentInit } from "./types";
|
|
6
|
+
import { normalizeEnvironment } from "./utils";
|
|
7
|
+
|
|
8
|
+
interface Props extends PhonePeCredentials {
|
|
9
|
+
request: PhonePePaymentInit;
|
|
10
|
+
onSuccess: (data: any) => void;
|
|
11
|
+
onError: (err: any) => void;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const PhonePeCheckoutNative: React.FC<Props> = ({
|
|
15
|
+
clientId,
|
|
16
|
+
clientSecret,
|
|
17
|
+
environment,
|
|
18
|
+
request,
|
|
19
|
+
onSuccess,
|
|
20
|
+
onError
|
|
21
|
+
}) => {
|
|
22
|
+
const [url, setUrl] = useState<string>();
|
|
23
|
+
const env = normalizeEnvironment(environment);
|
|
24
|
+
const urls = PhonePeUrls[env];
|
|
25
|
+
|
|
26
|
+
if (!urls) {
|
|
27
|
+
onError(`Invalid environment: ${environment}`);
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
useEffect(() => {
|
|
32
|
+
init();
|
|
33
|
+
}, []);
|
|
34
|
+
|
|
35
|
+
const init = async () => {
|
|
36
|
+
try {
|
|
37
|
+
const tokenResp = await fetch(urls.token, {
|
|
38
|
+
method: "POST",
|
|
39
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
40
|
+
body: new URLSearchParams({
|
|
41
|
+
client_id: clientId,
|
|
42
|
+
client_secret: clientSecret,
|
|
43
|
+
grant_type: "client_credentials",
|
|
44
|
+
client_version: "1"
|
|
45
|
+
})
|
|
46
|
+
});
|
|
47
|
+
const token = (await tokenResp.json()).access_token;
|
|
48
|
+
|
|
49
|
+
const payload = {
|
|
50
|
+
...request,
|
|
51
|
+
merchantOrderId: v4(),
|
|
52
|
+
merchantTransactionId: v4()
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const payResp = await fetch(urls.pay, {
|
|
56
|
+
method: "POST",
|
|
57
|
+
headers: {
|
|
58
|
+
"Content-Type": "application/json",
|
|
59
|
+
Authorization: `O-Bearer ${token}`
|
|
60
|
+
},
|
|
61
|
+
body: JSON.stringify(payload)
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
const { redirectUrl } = await payResp.json();
|
|
65
|
+
setUrl(redirectUrl);
|
|
66
|
+
} catch (err) {
|
|
67
|
+
onError(err);
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
return url ? (
|
|
72
|
+
<WebView
|
|
73
|
+
source={{ uri: url }}
|
|
74
|
+
onNavigationStateChange={(nav) => {
|
|
75
|
+
if (nav.url.includes("callback")) {
|
|
76
|
+
onSuccess(nav);
|
|
77
|
+
}
|
|
78
|
+
}}
|
|
79
|
+
/>
|
|
80
|
+
) : null;
|
|
81
|
+
};
|
package/src/env.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export const PhonePeUrls = {
|
|
2
|
+
production: {
|
|
3
|
+
token: "https://api.phonepe.com/apis/identity-manager/v1/oauth/token",
|
|
4
|
+
pay: "https://api.phonepe.com/apis/pg/checkout/v2/pay",
|
|
5
|
+
status: "https://api.phonepe.com/apis/pg/checkout/v2/order/status",
|
|
6
|
+
script: "https://mercury.phonepe.com/web/bundle/checkout.js",
|
|
7
|
+
},
|
|
8
|
+
|
|
9
|
+
sandbox: {
|
|
10
|
+
token: "https://api-preprod.phonepe.com/apis/identity-manager/v1/oauth/token",
|
|
11
|
+
pay: "https://api-preprod.phonepe.com/apis/pg-sandbox/checkout/v2/pay",
|
|
12
|
+
status: "https://api-preprod.phonepe.com/apis/pg-sandbox/checkout/v2/order/status",
|
|
13
|
+
script: "https://mercury.phonepe.com/web/bundle/checkout.js",
|
|
14
|
+
}
|
|
15
|
+
};
|
package/src/index.ts
ADDED
package/src/native.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./PhonePeReactNative";
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export type PhonePeEnvironment = "Production" | "Sandbox";
|
|
2
|
+
|
|
3
|
+
export interface PhonePeCredentials {
|
|
4
|
+
clientId: string;
|
|
5
|
+
clientSecret: string;
|
|
6
|
+
environment: PhonePeEnvironment;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface PhonePePaymentInit {
|
|
10
|
+
merchantId: string;
|
|
11
|
+
amount: number;
|
|
12
|
+
mobileNumber?: string;
|
|
13
|
+
redirectUrl: string;
|
|
14
|
+
callbackUrl: string;
|
|
15
|
+
deviceContext?: any;
|
|
16
|
+
paymentScope?: string;
|
|
17
|
+
}
|
package/src/utils.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { PhonePeEnvironment } from "./types";
|
|
2
|
+
|
|
3
|
+
export function normalizeEnvironment(env: PhonePeEnvironment): "production" | "sandbox" {
|
|
4
|
+
const e = String(env).toLowerCase().trim();
|
|
5
|
+
|
|
6
|
+
if (e === "production" || e === "prod") return "production";
|
|
7
|
+
if (e === "sandbox") return "sandbox";
|
|
8
|
+
|
|
9
|
+
throw new Error(
|
|
10
|
+
`Invalid environment "${env}". Expected "Production" or "Sandbox".`
|
|
11
|
+
);
|
|
12
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2020",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"declaration": true,
|
|
6
|
+
"jsx": "react-jsx",
|
|
7
|
+
"esModuleInterop": true,
|
|
8
|
+
"allowSyntheticDefaultImports": true,
|
|
9
|
+
"skipLibCheck": true,
|
|
10
|
+
"moduleResolution": "Node",
|
|
11
|
+
"outDir": "dist"
|
|
12
|
+
},
|
|
13
|
+
"include": ["src"]
|
|
14
|
+
}
|