pesafy 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/README.md +177 -0
- package/dist/components/react/index.cjs +230 -0
- package/dist/components/react/index.cjs.map +1 -0
- package/dist/components/react/index.d.cts +63 -0
- package/dist/components/react/index.d.ts +63 -0
- package/dist/components/react/index.js +200 -0
- package/dist/components/react/index.js.map +1 -0
- package/dist/components/react/styles.css +90 -0
- package/dist/components/react/styles.css.map +1 -0
- package/dist/components/react/styles.d.cts +2 -0
- package/dist/components/react/styles.d.ts +2 -0
- package/dist/index.cjs +729 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +416 -0
- package/dist/index.d.ts +416 -0
- package/dist/index.js +717 -0
- package/dist/index.js.map +1 -0
- package/package.json +105 -0
- package/src/components/vue/PaymentButton.vue +71 -0
- package/src/components/vue/PaymentForm.vue +164 -0
- package/src/components/vue/PaymentStatus.vue +68 -0
- package/src/components/vue/QRCode.vue +39 -0
- package/src/components/vue/index.ts +13 -0
- package/src/components/vue/shims-vue.d.ts +39 -0
- package/src/components/vue/types.ts +15 -0
package/README.md
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
# Pesafy ๐ณ
|
|
2
|
+
|
|
3
|
+
> A powerful, type-safe payment gateway library for African payment systems, starting with M-Pesa Daraja API
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/pesafy)
|
|
6
|
+
[](https://opensource.org/licenses/MIT)
|
|
7
|
+
[](https://www.typescriptlang.org/)
|
|
8
|
+
|
|
9
|
+
Pesafy simplifies payment processing for Kenyan merchants by providing a clean, well-documented, and easy-to-use interface for M-Pesa transactions. Built with TypeScript, following industry best practices inspired by Stripe.
|
|
10
|
+
|
|
11
|
+
## โจ Features
|
|
12
|
+
|
|
13
|
+
- ๐ **Easy Integration**: Simple, intuitive API design
|
|
14
|
+
- ๐ **Type-Safe**: Full TypeScript support with comprehensive types
|
|
15
|
+
- ๐ฏ **Complete M-Pesa Coverage**: STK Push, B2C, B2B, C2B, QR Codes, and more
|
|
16
|
+
- ๐ **Secure**: Built-in security credential encryption and webhook verification
|
|
17
|
+
- ๐จ **Dashboard Ready**: Payment monitoring and webhook management dashboard
|
|
18
|
+
- ๐งฉ **Component Library**: Reusable payment components for React/Vue
|
|
19
|
+
- ๐ **Well Documented**: Comprehensive documentation and examples
|
|
20
|
+
- โก **Fast**: Built with Bun for optimal performance
|
|
21
|
+
|
|
22
|
+
## ๐ฆ Installation
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
# Using npm
|
|
26
|
+
npm install pesafy
|
|
27
|
+
|
|
28
|
+
# Using yarn
|
|
29
|
+
yarn add pesafy
|
|
30
|
+
|
|
31
|
+
# Using pnpm
|
|
32
|
+
pnpm add pesafy
|
|
33
|
+
|
|
34
|
+
# Using bun
|
|
35
|
+
bun add pesafy
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## ๐ Quick Start
|
|
39
|
+
|
|
40
|
+
```typescript
|
|
41
|
+
import { Mpesa } from "pesafy";
|
|
42
|
+
|
|
43
|
+
const mpesa = new Mpesa({
|
|
44
|
+
consumerKey: "your-consumer-key",
|
|
45
|
+
consumerSecret: "your-consumer-secret",
|
|
46
|
+
environment: "sandbox",
|
|
47
|
+
lipaNaMpesaShortCode: "174379",
|
|
48
|
+
lipaNaMpesaPassKey: "your-passkey",
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
// STK Push (M-Pesa Express)
|
|
52
|
+
const result = await mpesa.stkPush({
|
|
53
|
+
amount: 100,
|
|
54
|
+
phoneNumber: "254712345678",
|
|
55
|
+
callbackUrl: "https://yoursite.com/callback",
|
|
56
|
+
accountReference: "ORDER-123",
|
|
57
|
+
transactionDesc: "Payment for order",
|
|
58
|
+
});
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## ๐ Documentation
|
|
62
|
+
|
|
63
|
+
- **[Getting Started Guide](./docs/guides/getting-started.md)** - Learn how to set up Pesafy
|
|
64
|
+
- **[API Reference](./docs/api/)** - Complete API documentation
|
|
65
|
+
- **[Examples](./docs/examples/)** - Code examples for different frameworks
|
|
66
|
+
- **[Architecture](./ARCHITECTURE.md)** - System architecture overview
|
|
67
|
+
- **[Project Plan](./PROJECT_PLAN.md)** - Development roadmap
|
|
68
|
+
|
|
69
|
+
## ๐ฏ Supported APIs
|
|
70
|
+
|
|
71
|
+
### โ
Available
|
|
72
|
+
|
|
73
|
+
- **STK Push (M-Pesa Express)** - Initiate payments via STK Push
|
|
74
|
+
- **STK Query** - Check STK Push transaction status
|
|
75
|
+
- **B2C** - Business to Customer payments
|
|
76
|
+
- **B2B** - Business to Business payments
|
|
77
|
+
- **C2B** - Register URLs & simulate (sandbox)
|
|
78
|
+
- **Dynamic QR Codes** - Generate LIPA NA M-PESA QR codes
|
|
79
|
+
- **Transaction Status** - Query transaction status
|
|
80
|
+
- **Reversal** - Reverse transactions
|
|
81
|
+
|
|
82
|
+
### ๐ฆ Components
|
|
83
|
+
|
|
84
|
+
- **PaymentButton** - Simple button to trigger payments
|
|
85
|
+
- **PaymentForm** - Complete form for collecting payment details
|
|
86
|
+
- **QRCode** - Display M-Pesa dynamic QR codes
|
|
87
|
+
- **PaymentStatus** - Show payment status with visual feedback
|
|
88
|
+
|
|
89
|
+
## ๐๏ธ Project Structure
|
|
90
|
+
|
|
91
|
+
```
|
|
92
|
+
pesafy/
|
|
93
|
+
โโโ src/ # Source code
|
|
94
|
+
โโโ docs/ # Documentation
|
|
95
|
+
โโโ tests/ # Test files
|
|
96
|
+
โโโ examples/ # Example projects
|
|
97
|
+
โโโ components/ # Payment components (coming soon)
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
See [FOLDER_STRUCTURE.md](./FOLDER_STRUCTURE.md) for detailed structure.
|
|
101
|
+
|
|
102
|
+
## ๐งช Development
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
# Install dependencies
|
|
106
|
+
bun install
|
|
107
|
+
|
|
108
|
+
# Run tests
|
|
109
|
+
bun test
|
|
110
|
+
|
|
111
|
+
# Build the library
|
|
112
|
+
bun run build
|
|
113
|
+
|
|
114
|
+
# Run type checking
|
|
115
|
+
bun run typecheck
|
|
116
|
+
|
|
117
|
+
# Format code
|
|
118
|
+
bun run format
|
|
119
|
+
|
|
120
|
+
# Lint code
|
|
121
|
+
bun run lint
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## ๐ Dashboard (SaaS)
|
|
125
|
+
|
|
126
|
+
Run the payment monitoring dashboard:
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
cd dashboard
|
|
130
|
+
bun install
|
|
131
|
+
bun run dev
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Open http://localhost:3000 to view the dashboard. See [RUNNING.md](./RUNNING.md) for full instructions.
|
|
135
|
+
|
|
136
|
+
## ๐ Contributing
|
|
137
|
+
|
|
138
|
+
Contributions are welcome! Please read our contributing guidelines before submitting PRs.
|
|
139
|
+
|
|
140
|
+
1. Fork the repository
|
|
141
|
+
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
|
|
142
|
+
3. Commit your changes (`git commit -m 'Add some amazing feature'`)
|
|
143
|
+
4. Push to the branch (`git push origin feature/amazing-feature`)
|
|
144
|
+
5. Open a Pull Request
|
|
145
|
+
|
|
146
|
+
## ๐ Security
|
|
147
|
+
|
|
148
|
+
For security concerns, please email [lewisodero27@gmail.com](mailto:lewisodero27@gmail.com) instead of using the issue tracker.
|
|
149
|
+
|
|
150
|
+
## ๐ License
|
|
151
|
+
|
|
152
|
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
|
153
|
+
|
|
154
|
+
## ๐ Acknowledgments
|
|
155
|
+
|
|
156
|
+
- [Safaricom Daraja API](https://developer.safaricom.co.ke/) - For providing the M-Pesa API
|
|
157
|
+
- Inspired by payment gateway patterns from Stripe and other industry leaders
|
|
158
|
+
|
|
159
|
+
## ๐ Support
|
|
160
|
+
|
|
161
|
+
- ๐ง Email: [lewisodero27@gmail.com](mailto:lewisodero27@gmail.com)
|
|
162
|
+
- ๐ Issues: [GitHub Issues](https://github.com/levos-snr/pesafy/issues)
|
|
163
|
+
- ๐ Documentation: [Full Documentation](./docs/)
|
|
164
|
+
|
|
165
|
+
## ๐บ๏ธ Roadmap
|
|
166
|
+
|
|
167
|
+
See [PROJECT_PLAN.md](./PROJECT_PLAN.md) for the complete development roadmap.
|
|
168
|
+
|
|
169
|
+
**Phase 1** (Current): Core library foundation โ
|
|
170
|
+
**Phase 2**: Payment processing implementation ๐ง
|
|
171
|
+
**Phase 3**: Webhook management system ๐
|
|
172
|
+
**Phase 4**: Dashboard development ๐
|
|
173
|
+
**Phase 5**: Payment components ๐
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
Made with โค๏ธ for the African developer community
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/components/react/index.tsx
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
PaymentButton: () => PaymentButton,
|
|
24
|
+
PaymentForm: () => PaymentForm,
|
|
25
|
+
PaymentStatus: () => PaymentStatus,
|
|
26
|
+
QRCode: () => QRCode
|
|
27
|
+
});
|
|
28
|
+
module.exports = __toCommonJS(index_exports);
|
|
29
|
+
|
|
30
|
+
// src/components/react/PaymentButton.tsx
|
|
31
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
32
|
+
function PaymentButton({
|
|
33
|
+
amount,
|
|
34
|
+
onPay,
|
|
35
|
+
disabled = false,
|
|
36
|
+
loading = false,
|
|
37
|
+
children,
|
|
38
|
+
className = ""
|
|
39
|
+
}) {
|
|
40
|
+
const handleClick = async () => {
|
|
41
|
+
if (disabled || loading) return;
|
|
42
|
+
await onPay({ amount });
|
|
43
|
+
};
|
|
44
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
45
|
+
"button",
|
|
46
|
+
{
|
|
47
|
+
type: "button",
|
|
48
|
+
onClick: handleClick,
|
|
49
|
+
disabled: disabled || loading,
|
|
50
|
+
className: `pesafy-payment-btn ${className}`,
|
|
51
|
+
"aria-busy": loading,
|
|
52
|
+
children: loading ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "pesafy-payment-btn-loading", children: "Processing..." }) : children ?? `Pay KES ${amount.toLocaleString()}`
|
|
53
|
+
}
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// src/components/react/PaymentForm.tsx
|
|
58
|
+
var import_react = require("react");
|
|
59
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
60
|
+
function PaymentForm({
|
|
61
|
+
onSubmit,
|
|
62
|
+
defaultAmount = 0,
|
|
63
|
+
defaultReference = "",
|
|
64
|
+
disabled = false,
|
|
65
|
+
loading = false,
|
|
66
|
+
className = ""
|
|
67
|
+
}) {
|
|
68
|
+
const [amount, setAmount] = (0, import_react.useState)(defaultAmount.toString());
|
|
69
|
+
const [phone, setPhone] = (0, import_react.useState)("");
|
|
70
|
+
const [reference, setReference] = (0, import_react.useState)(defaultReference);
|
|
71
|
+
const [desc, setDesc] = (0, import_react.useState)("Payment");
|
|
72
|
+
const handleSubmit = async (e) => {
|
|
73
|
+
e.preventDefault();
|
|
74
|
+
if (disabled || loading) return;
|
|
75
|
+
const amt = Number.parseFloat(amount);
|
|
76
|
+
if (Number.isNaN(amt) || amt <= 0) return;
|
|
77
|
+
const cleaned = phone.replace(/\D/g, "");
|
|
78
|
+
if (cleaned.length < 9) return;
|
|
79
|
+
await onSubmit({
|
|
80
|
+
amount: amt,
|
|
81
|
+
phoneNumber: phone,
|
|
82
|
+
accountReference: reference || `REF-${Date.now()}`,
|
|
83
|
+
transactionDesc: desc
|
|
84
|
+
});
|
|
85
|
+
};
|
|
86
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
87
|
+
"form",
|
|
88
|
+
{
|
|
89
|
+
onSubmit: handleSubmit,
|
|
90
|
+
className: `pesafy-payment-form ${className}`,
|
|
91
|
+
children: [
|
|
92
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "pesafy-payment-form-field", children: [
|
|
93
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { htmlFor: "pesafy-amount", children: "Amount (KES)" }),
|
|
94
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
95
|
+
"input",
|
|
96
|
+
{
|
|
97
|
+
id: "pesafy-amount",
|
|
98
|
+
type: "number",
|
|
99
|
+
min: "1",
|
|
100
|
+
step: "0.01",
|
|
101
|
+
value: amount,
|
|
102
|
+
onChange: (e) => setAmount(e.target.value),
|
|
103
|
+
placeholder: "100",
|
|
104
|
+
disabled,
|
|
105
|
+
required: true
|
|
106
|
+
}
|
|
107
|
+
)
|
|
108
|
+
] }),
|
|
109
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "pesafy-payment-form-field", children: [
|
|
110
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { htmlFor: "pesafy-phone", children: "M-Pesa Phone Number" }),
|
|
111
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
112
|
+
"input",
|
|
113
|
+
{
|
|
114
|
+
id: "pesafy-phone",
|
|
115
|
+
type: "tel",
|
|
116
|
+
value: phone,
|
|
117
|
+
onChange: (e) => setPhone(e.target.value),
|
|
118
|
+
placeholder: "07XX XXX XXX or 2547XX XXX XXX",
|
|
119
|
+
disabled,
|
|
120
|
+
required: true
|
|
121
|
+
}
|
|
122
|
+
)
|
|
123
|
+
] }),
|
|
124
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "pesafy-payment-form-field", children: [
|
|
125
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { htmlFor: "pesafy-reference", children: "Reference" }),
|
|
126
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
127
|
+
"input",
|
|
128
|
+
{
|
|
129
|
+
id: "pesafy-reference",
|
|
130
|
+
type: "text",
|
|
131
|
+
value: reference,
|
|
132
|
+
onChange: (e) => setReference(e.target.value),
|
|
133
|
+
placeholder: "ORDER-123",
|
|
134
|
+
disabled,
|
|
135
|
+
maxLength: 12
|
|
136
|
+
}
|
|
137
|
+
)
|
|
138
|
+
] }),
|
|
139
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "pesafy-payment-form-field", children: [
|
|
140
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { htmlFor: "pesafy-desc", children: "Description" }),
|
|
141
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
142
|
+
"input",
|
|
143
|
+
{
|
|
144
|
+
id: "pesafy-desc",
|
|
145
|
+
type: "text",
|
|
146
|
+
value: desc,
|
|
147
|
+
onChange: (e) => setDesc(e.target.value),
|
|
148
|
+
placeholder: "Payment",
|
|
149
|
+
disabled,
|
|
150
|
+
maxLength: 13
|
|
151
|
+
}
|
|
152
|
+
)
|
|
153
|
+
] }),
|
|
154
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
155
|
+
"button",
|
|
156
|
+
{
|
|
157
|
+
type: "submit",
|
|
158
|
+
disabled: disabled || loading,
|
|
159
|
+
className: "pesafy-payment-form-submit",
|
|
160
|
+
"aria-busy": loading,
|
|
161
|
+
children: loading ? "Processing..." : "Pay with M-Pesa"
|
|
162
|
+
}
|
|
163
|
+
)
|
|
164
|
+
]
|
|
165
|
+
}
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// src/components/react/PaymentStatus.tsx
|
|
170
|
+
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
171
|
+
function PaymentStatus({
|
|
172
|
+
status,
|
|
173
|
+
message,
|
|
174
|
+
transactionId,
|
|
175
|
+
children,
|
|
176
|
+
className = ""
|
|
177
|
+
}) {
|
|
178
|
+
if (status === "idle" && !children) return null;
|
|
179
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
180
|
+
"div",
|
|
181
|
+
{
|
|
182
|
+
className: `pesafy-payment-status pesafy-payment-status--${status} ${className}`,
|
|
183
|
+
role: "status",
|
|
184
|
+
"aria-live": "polite",
|
|
185
|
+
children: [
|
|
186
|
+
status === "pending" && (children ?? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: "Waiting for payment..." })),
|
|
187
|
+
status === "success" && (children ?? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { children: [
|
|
188
|
+
"Payment successful",
|
|
189
|
+
transactionId && ` (${transactionId})`,
|
|
190
|
+
message && ` - ${message}`
|
|
191
|
+
] })),
|
|
192
|
+
status === "error" && (children ?? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { children: [
|
|
193
|
+
"Payment failed",
|
|
194
|
+
message && ` - ${message}`
|
|
195
|
+
] })),
|
|
196
|
+
status === "idle" && children
|
|
197
|
+
]
|
|
198
|
+
}
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// src/components/react/QRCode.tsx
|
|
203
|
+
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
204
|
+
function QRCode({
|
|
205
|
+
base64,
|
|
206
|
+
alt = "M-Pesa QR Code",
|
|
207
|
+
size = 300,
|
|
208
|
+
className = ""
|
|
209
|
+
}) {
|
|
210
|
+
const src = base64.startsWith("data:") ? base64 : `data:image/png;base64,${base64}`;
|
|
211
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
212
|
+
"img",
|
|
213
|
+
{
|
|
214
|
+
src,
|
|
215
|
+
alt,
|
|
216
|
+
width: size,
|
|
217
|
+
height: size,
|
|
218
|
+
className: `pesafy-qrcode ${className}`,
|
|
219
|
+
loading: "lazy"
|
|
220
|
+
}
|
|
221
|
+
);
|
|
222
|
+
}
|
|
223
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
224
|
+
0 && (module.exports = {
|
|
225
|
+
PaymentButton,
|
|
226
|
+
PaymentForm,
|
|
227
|
+
PaymentStatus,
|
|
228
|
+
QRCode
|
|
229
|
+
});
|
|
230
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/components/react/index.tsx","../../../src/components/react/PaymentButton.tsx","../../../src/components/react/PaymentForm.tsx","../../../src/components/react/PaymentStatus.tsx","../../../src/components/react/QRCode.tsx"],"sourcesContent":["/**\n * Pesafy React Components\n * Use with your backend API - never expose Daraja credentials in the browser\n *\n * Import styles: import \"pesafy/components/react/styles.css\"\n */\n\nexport type { PaymentButtonProps } from \"./PaymentButton\";\nexport { PaymentButton } from \"./PaymentButton\";\nexport type { PaymentFormData, PaymentFormProps } from \"./PaymentForm\";\nexport { PaymentForm } from \"./PaymentForm\";\nexport type { PaymentStatusProps, PaymentStatusState } from \"./PaymentStatus\";\nexport { PaymentStatus } from \"./PaymentStatus\";\nexport type { QRCodeProps } from \"./QRCode\";\nexport { QRCode } from \"./QRCode\";\n","/**\n * PaymentButton - Trigger M-Pesa STK Push payment\n * Call onPay with payment details; parent handles API call (credentials stay server-side)\n */\n\nimport type { ReactNode } from \"react\";\n\nexport interface PaymentButtonProps {\n amount: number;\n /** Called when user clicks pay. Parent should call your backend which uses Pesafy */\n onPay: (params: { amount: number }) => void | Promise<void>;\n disabled?: boolean;\n loading?: boolean;\n children?: ReactNode;\n className?: string;\n}\n\nexport function PaymentButton({\n amount,\n onPay,\n disabled = false,\n loading = false,\n children,\n className = \"\",\n}: PaymentButtonProps) {\n const handleClick = async () => {\n if (disabled || loading) return;\n await onPay({ amount });\n };\n\n return (\n <button\n type=\"button\"\n onClick={handleClick}\n disabled={disabled || loading}\n className={`pesafy-payment-btn ${className}`}\n aria-busy={loading}\n >\n {loading ? (\n <span className=\"pesafy-payment-btn-loading\">Processing...</span>\n ) : (\n (children ?? `Pay KES ${amount.toLocaleString()}`)\n )}\n </button>\n );\n}\n","/**\n * PaymentForm - Collect payment details for M-Pesa STK Push\n * Parent receives form data and handles API call (credentials stay server-side)\n */\n\nimport { type FormEvent, useState } from \"react\";\n\nexport interface PaymentFormData {\n amount: number;\n phoneNumber: string;\n accountReference: string;\n transactionDesc: string;\n}\n\nexport interface PaymentFormProps {\n /** Called on submit. Parent should call your backend which uses Pesafy stkPush */\n onSubmit: (data: PaymentFormData) => void | Promise<void>;\n defaultAmount?: number;\n defaultReference?: string;\n disabled?: boolean;\n loading?: boolean;\n className?: string;\n}\n\nexport function PaymentForm({\n onSubmit,\n defaultAmount = 0,\n defaultReference = \"\",\n disabled = false,\n loading = false,\n className = \"\",\n}: PaymentFormProps) {\n const [amount, setAmount] = useState(defaultAmount.toString());\n const [phone, setPhone] = useState(\"\");\n const [reference, setReference] = useState(defaultReference);\n const [desc, setDesc] = useState(\"Payment\");\n\n const handleSubmit = async (e: FormEvent) => {\n e.preventDefault();\n if (disabled || loading) return;\n\n const amt = Number.parseFloat(amount);\n if (Number.isNaN(amt) || amt <= 0) return;\n\n const cleaned = phone.replace(/\\D/g, \"\");\n if (cleaned.length < 9) return;\n\n await onSubmit({\n amount: amt,\n phoneNumber: phone,\n accountReference: reference || `REF-${Date.now()}`,\n transactionDesc: desc,\n });\n };\n\n return (\n <form\n onSubmit={handleSubmit}\n className={`pesafy-payment-form ${className}`}\n >\n <div className=\"pesafy-payment-form-field\">\n <label htmlFor=\"pesafy-amount\">Amount (KES)</label>\n <input\n id=\"pesafy-amount\"\n type=\"number\"\n min=\"1\"\n step=\"0.01\"\n value={amount}\n onChange={(e) => setAmount((e.target as HTMLInputElement).value)}\n placeholder=\"100\"\n disabled={disabled}\n required\n />\n </div>\n <div className=\"pesafy-payment-form-field\">\n <label htmlFor=\"pesafy-phone\">M-Pesa Phone Number</label>\n <input\n id=\"pesafy-phone\"\n type=\"tel\"\n value={phone}\n onChange={(e) => setPhone((e.target as HTMLInputElement).value)}\n placeholder=\"07XX XXX XXX or 2547XX XXX XXX\"\n disabled={disabled}\n required\n />\n </div>\n <div className=\"pesafy-payment-form-field\">\n <label htmlFor=\"pesafy-reference\">Reference</label>\n <input\n id=\"pesafy-reference\"\n type=\"text\"\n value={reference}\n onChange={(e) => setReference((e.target as HTMLInputElement).value)}\n placeholder=\"ORDER-123\"\n disabled={disabled}\n maxLength={12}\n />\n </div>\n <div className=\"pesafy-payment-form-field\">\n <label htmlFor=\"pesafy-desc\">Description</label>\n <input\n id=\"pesafy-desc\"\n type=\"text\"\n value={desc}\n onChange={(e) => setDesc((e.target as HTMLInputElement).value)}\n placeholder=\"Payment\"\n disabled={disabled}\n maxLength={13}\n />\n </div>\n <button\n type=\"submit\"\n disabled={disabled || loading}\n className=\"pesafy-payment-form-submit\"\n aria-busy={loading}\n >\n {loading ? \"Processing...\" : \"Pay with M-Pesa\"}\n </button>\n </form>\n );\n}\n","/**\n * PaymentStatus - Display payment state (pending, success, failed)\n */\n\nimport type { ReactNode } from \"react\";\n\nexport type PaymentStatusState = \"idle\" | \"pending\" | \"success\" | \"error\";\n\nexport interface PaymentStatusProps {\n status: PaymentStatusState;\n /** Optional message for success/error states */\n message?: string;\n /** Optional transaction/callback data */\n transactionId?: string;\n children?: ReactNode;\n className?: string;\n}\n\nexport function PaymentStatus({\n status,\n message,\n transactionId,\n children,\n className = \"\",\n}: PaymentStatusProps) {\n if (status === \"idle\" && !children) return null;\n\n return (\n <div\n className={`pesafy-payment-status pesafy-payment-status--${status} ${className}`}\n role=\"status\"\n aria-live=\"polite\"\n >\n {status === \"pending\" &&\n (children ?? <span>Waiting for payment...</span>)}\n {status === \"success\" &&\n (children ?? (\n <span>\n Payment successful\n {transactionId && ` (${transactionId})`}\n {message && ` - ${message}`}\n </span>\n ))}\n {status === \"error\" &&\n (children ?? (\n <span>\n Payment failed\n {message && ` - ${message}`}\n </span>\n ))}\n {status === \"idle\" && children}\n </div>\n );\n}\n","/**\n * QRCode - Display M-Pesa Dynamic QR code\n * Pass base64 image from mpesa.qrCode() response\n */\n\nexport interface QRCodeProps {\n /** Base64 QR code image from Dynamic QR API */\n base64: string;\n alt?: string;\n size?: number;\n className?: string;\n}\n\nexport function QRCode({\n base64,\n alt = \"M-Pesa QR Code\",\n size = 300,\n className = \"\",\n}: QRCodeProps) {\n const src = base64.startsWith(\"data:\")\n ? base64\n : `data:image/png;base64,${base64}`;\n\n return (\n <img\n src={src}\n alt={alt}\n width={size}\n height={size}\n className={`pesafy-qrcode ${className}`}\n loading=\"lazy\"\n />\n );\n}\n"],"mappings":"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","names":["import_jsx_runtime","import_jsx_runtime","import_jsx_runtime"]}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import { ReactNode } from 'react';
|
|
3
|
+
|
|
4
|
+
interface PaymentButtonProps {
|
|
5
|
+
amount: number;
|
|
6
|
+
/** Called when user clicks pay. Parent should call your backend which uses Pesafy */
|
|
7
|
+
onPay: (params: {
|
|
8
|
+
amount: number;
|
|
9
|
+
}) => void | Promise<void>;
|
|
10
|
+
disabled?: boolean;
|
|
11
|
+
loading?: boolean;
|
|
12
|
+
children?: ReactNode;
|
|
13
|
+
className?: string;
|
|
14
|
+
}
|
|
15
|
+
declare function PaymentButton({ amount, onPay, disabled, loading, children, className, }: PaymentButtonProps): react_jsx_runtime.JSX.Element;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* PaymentForm - Collect payment details for M-Pesa STK Push
|
|
19
|
+
* Parent receives form data and handles API call (credentials stay server-side)
|
|
20
|
+
*/
|
|
21
|
+
interface PaymentFormData {
|
|
22
|
+
amount: number;
|
|
23
|
+
phoneNumber: string;
|
|
24
|
+
accountReference: string;
|
|
25
|
+
transactionDesc: string;
|
|
26
|
+
}
|
|
27
|
+
interface PaymentFormProps {
|
|
28
|
+
/** Called on submit. Parent should call your backend which uses Pesafy stkPush */
|
|
29
|
+
onSubmit: (data: PaymentFormData) => void | Promise<void>;
|
|
30
|
+
defaultAmount?: number;
|
|
31
|
+
defaultReference?: string;
|
|
32
|
+
disabled?: boolean;
|
|
33
|
+
loading?: boolean;
|
|
34
|
+
className?: string;
|
|
35
|
+
}
|
|
36
|
+
declare function PaymentForm({ onSubmit, defaultAmount, defaultReference, disabled, loading, className, }: PaymentFormProps): react_jsx_runtime.JSX.Element;
|
|
37
|
+
|
|
38
|
+
type PaymentStatusState = "idle" | "pending" | "success" | "error";
|
|
39
|
+
interface PaymentStatusProps {
|
|
40
|
+
status: PaymentStatusState;
|
|
41
|
+
/** Optional message for success/error states */
|
|
42
|
+
message?: string;
|
|
43
|
+
/** Optional transaction/callback data */
|
|
44
|
+
transactionId?: string;
|
|
45
|
+
children?: ReactNode;
|
|
46
|
+
className?: string;
|
|
47
|
+
}
|
|
48
|
+
declare function PaymentStatus({ status, message, transactionId, children, className, }: PaymentStatusProps): react_jsx_runtime.JSX.Element | null;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* QRCode - Display M-Pesa Dynamic QR code
|
|
52
|
+
* Pass base64 image from mpesa.qrCode() response
|
|
53
|
+
*/
|
|
54
|
+
interface QRCodeProps {
|
|
55
|
+
/** Base64 QR code image from Dynamic QR API */
|
|
56
|
+
base64: string;
|
|
57
|
+
alt?: string;
|
|
58
|
+
size?: number;
|
|
59
|
+
className?: string;
|
|
60
|
+
}
|
|
61
|
+
declare function QRCode({ base64, alt, size, className, }: QRCodeProps): react_jsx_runtime.JSX.Element;
|
|
62
|
+
|
|
63
|
+
export { PaymentButton, type PaymentButtonProps, PaymentForm, type PaymentFormData, type PaymentFormProps, PaymentStatus, type PaymentStatusProps, type PaymentStatusState, QRCode, type QRCodeProps };
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import { ReactNode } from 'react';
|
|
3
|
+
|
|
4
|
+
interface PaymentButtonProps {
|
|
5
|
+
amount: number;
|
|
6
|
+
/** Called when user clicks pay. Parent should call your backend which uses Pesafy */
|
|
7
|
+
onPay: (params: {
|
|
8
|
+
amount: number;
|
|
9
|
+
}) => void | Promise<void>;
|
|
10
|
+
disabled?: boolean;
|
|
11
|
+
loading?: boolean;
|
|
12
|
+
children?: ReactNode;
|
|
13
|
+
className?: string;
|
|
14
|
+
}
|
|
15
|
+
declare function PaymentButton({ amount, onPay, disabled, loading, children, className, }: PaymentButtonProps): react_jsx_runtime.JSX.Element;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* PaymentForm - Collect payment details for M-Pesa STK Push
|
|
19
|
+
* Parent receives form data and handles API call (credentials stay server-side)
|
|
20
|
+
*/
|
|
21
|
+
interface PaymentFormData {
|
|
22
|
+
amount: number;
|
|
23
|
+
phoneNumber: string;
|
|
24
|
+
accountReference: string;
|
|
25
|
+
transactionDesc: string;
|
|
26
|
+
}
|
|
27
|
+
interface PaymentFormProps {
|
|
28
|
+
/** Called on submit. Parent should call your backend which uses Pesafy stkPush */
|
|
29
|
+
onSubmit: (data: PaymentFormData) => void | Promise<void>;
|
|
30
|
+
defaultAmount?: number;
|
|
31
|
+
defaultReference?: string;
|
|
32
|
+
disabled?: boolean;
|
|
33
|
+
loading?: boolean;
|
|
34
|
+
className?: string;
|
|
35
|
+
}
|
|
36
|
+
declare function PaymentForm({ onSubmit, defaultAmount, defaultReference, disabled, loading, className, }: PaymentFormProps): react_jsx_runtime.JSX.Element;
|
|
37
|
+
|
|
38
|
+
type PaymentStatusState = "idle" | "pending" | "success" | "error";
|
|
39
|
+
interface PaymentStatusProps {
|
|
40
|
+
status: PaymentStatusState;
|
|
41
|
+
/** Optional message for success/error states */
|
|
42
|
+
message?: string;
|
|
43
|
+
/** Optional transaction/callback data */
|
|
44
|
+
transactionId?: string;
|
|
45
|
+
children?: ReactNode;
|
|
46
|
+
className?: string;
|
|
47
|
+
}
|
|
48
|
+
declare function PaymentStatus({ status, message, transactionId, children, className, }: PaymentStatusProps): react_jsx_runtime.JSX.Element | null;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* QRCode - Display M-Pesa Dynamic QR code
|
|
52
|
+
* Pass base64 image from mpesa.qrCode() response
|
|
53
|
+
*/
|
|
54
|
+
interface QRCodeProps {
|
|
55
|
+
/** Base64 QR code image from Dynamic QR API */
|
|
56
|
+
base64: string;
|
|
57
|
+
alt?: string;
|
|
58
|
+
size?: number;
|
|
59
|
+
className?: string;
|
|
60
|
+
}
|
|
61
|
+
declare function QRCode({ base64, alt, size, className, }: QRCodeProps): react_jsx_runtime.JSX.Element;
|
|
62
|
+
|
|
63
|
+
export { PaymentButton, type PaymentButtonProps, PaymentForm, type PaymentFormData, type PaymentFormProps, PaymentStatus, type PaymentStatusProps, type PaymentStatusState, QRCode, type QRCodeProps };
|