├── .babelrc.js
├── .eslintignore
├── .eslintrc
├── .github
├── ISSUE_TEMPLATE
│ ├── bug_report.md
│ └── config.yml
└── workflows
│ └── publish.yml
├── .gitignore
├── .prettierignore
├── CONTRIBUTING.md
├── LICENSE.md
├── README.md
├── React_App.png
├── dist
├── FWButton.d.ts
├── closeModal.d.ts
├── index.d.ts
├── index.es.js
├── index.js
├── script.d.ts
├── types.d.ts
└── useFW.d.ts
├── example
├── .gitignore
├── README.md
├── package-lock.json
├── package.json
├── public
│ ├── favicon.ico
│ ├── index.html
│ ├── logo192.png
│ ├── logo512.png
│ ├── manifest.json
│ └── robots.txt
└── src
│ ├── App.css
│ ├── App.js
│ ├── dist
│ ├── FWButton.d.ts
│ ├── closeModal.d.ts
│ ├── index.d.ts
│ ├── index.es.js
│ ├── index.js
│ ├── script.d.ts
│ ├── types.d.ts
│ └── useFW.d.ts
│ ├── index.css
│ ├── index.js
│ ├── logo.svg
│ └── setupTests.js
├── package-lock.json
├── package.json
├── rollup.config.js
├── src
├── FWButton.tsx
├── closeModal.tsx
├── index.ts
├── script.ts
├── types.ts
└── useFW.tsx
├── test
├── .nycrc
└── flutterwave.spec.ts
└── tsconfig.json
/.babelrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: [
3 | [
4 | '@babel/preset-env',
5 | {
6 | targets: {
7 | node: 'current',
8 | },
9 | },
10 | ],
11 | '@babel/preset-typescript',
12 | ],
13 |
14 | };
15 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | dist
2 | node_modules
3 | coverage
4 | build
5 | docs
6 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": [
3 | "plugin:@typescript-eslint/recommended",
4 | "prettier",
5 | "prettier/@typescript-eslint",
6 | "eslint-config-prettier"
7 | ],
8 | "parser": "@typescript-eslint/parser",
9 | "parserOptions": {
10 | "ecmaVersion": 2019,
11 | "sourceType": "module"
12 | },
13 | "plugins": ["@typescript-eslint", "prettier"],
14 | "globals": {
15 | "cy": "readonly",
16 | "assert": "readonly",
17 | "context": "readonly",
18 | "Atomics": "readonly",
19 | "SharedArrayBuffer": "readonly"
20 | },
21 | "rules": {
22 | "quotes": ["error", "single"],
23 | "semi": ["error", "always"],
24 | "no-undef": "off",
25 | "no-empty": "warn",
26 | "no-console": "error",
27 | "no-func-assign": 1,
28 | "no-unreachable": 1,
29 | "no-invalid-regexp": 1,
30 | "no-unused-vars": "off",
31 | "jsx-a11y/href-no-hash": "off",
32 | "@typescript-eslint/camelcase": "off",
33 | "@typescript-eslint/ban-ts-ignore": "off",
34 | "@typescript-eslint/no-empty-function": "warn",
35 | "@typescript-eslint/no-use-before-define": "off",
36 | "@typescript-eslint/explicit-member-accessibility": "off",
37 | "@typescript-eslint/explicit-function-return-type": "off"
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: bug
6 | assignees: ''
7 |
8 | ---
9 |
10 | Have you read our [Code of Conduct](https://github.com/Flutterwave/React/blob/master/CONTRIBUTING.md)? By filing an Issue, you are expected to comply with it, including treating everyone with respect
11 |
12 | Do you want to ask a question? Are you looking for support? The developer slack is the best place for getting [support](https://bit.ly/34Vkzcg)
13 |
14 | ### Description
15 |
16 |
17 |
18 | ### Steps to Reproduce
19 |
20 | 1.
21 | 2.
22 | 3.
23 |
24 | **Expected behaviour:**
25 |
26 |
27 |
28 | **Actual behaviour:**
29 |
30 |
31 |
32 | **Reproduces how often:**
33 |
34 |
35 |
36 | ### Configuration
37 |
38 | - API Version:
39 | - Environment:
40 | - Browser:
41 | - Language:
42 |
43 | ### Additional Information
44 |
45 |
46 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/config.yml:
--------------------------------------------------------------------------------
1 | blank_issues_enabled: false
2 | contact_links:
3 | - name: Developer Support Forum
4 | url: https://forum.flutterwave.com
5 | about: If you're having general trouble with your integration, Kindly contact our support team.
6 |
--------------------------------------------------------------------------------
/.github/workflows/publish.yml:
--------------------------------------------------------------------------------
1 | name: Publish React Package
2 |
3 | on:
4 | release:
5 | types: [created]
6 |
7 | jobs:
8 | build:
9 | runs-on: ubuntu-latest
10 | steps:
11 | - uses: actions/checkout@v2
12 | - uses: actions/setup-node@v1
13 | with:
14 | node-version: 12
15 | - run: npm i && npm run build
16 |
17 | publish-npm:
18 | needs: build
19 | runs-on: ubuntu-latest
20 | steps:
21 | - uses: actions/checkout@v2
22 | - uses: actions/setup-node@v1
23 | with:
24 | node-version: 12
25 | registry-url: https://registry.npmjs.org/
26 | - run: npm publish
27 | env:
28 | NODE_AUTH_TOKEN: ${{secrets.npm_token}}
29 |
30 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | lerna-debug.log*
8 |
9 | # Diagnostic reports (https://nodejs.org/api/report.html)
10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
11 |
12 | # Runtime data
13 | pids
14 | *.pid
15 | *.seed
16 | *.pid.lock
17 | .vscode
18 |
19 | # Directory for instrumented libs generated by jscoverage/JSCover
20 | lib-cov
21 |
22 | # Coverage directory used by tools like istanbul
23 | coverage
24 | *.lcov
25 |
26 | # nyc test coverage
27 | .nyc_output
28 |
29 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
30 | .grunt
31 |
32 | # Bower dependency directory (https://bower.io/)
33 | bower_components
34 |
35 | # node-waf configuration
36 | .lock-wscript
37 |
38 | # Compiled binary addons (https://nodejs.org/api/addons.html)
39 | build/Release
40 |
41 | # Dependency directories
42 | node_modules/
43 | jspm_packages/
44 |
45 | # TypeScript v1 declaration files
46 | typings/
47 |
48 | # TypeScript cache
49 | *.tsbuildinfo
50 |
51 | # Optional npm cache directory
52 | .npm
53 |
54 | # Optional eslint cache
55 | .eslintcache
56 |
57 | # Microbundle cache
58 | .rpt2_cache/
59 | .rts2_cache_cjs/
60 | .rts2_cache_es/
61 | .rts2_cache_umd/
62 |
63 | # Optional REPL history
64 | .node_repl_history
65 |
66 | # Output of 'npm pack'
67 | *.tgz
68 |
69 | # Yarn Integrity file
70 | .yarn-integrity
71 |
72 | # dotenv environment variables file
73 | .env
74 | .env.test
75 |
76 | # parcel-bundler cache (https://parceljs.org/)
77 | .cache
78 |
79 | # Next.js build output
80 | .next
81 |
82 | # Nuxt.js build / generate output
83 | .nuxt
84 |
85 | # Gatsby files
86 | .cache/
87 | # Comment in the public line in if your project uses Gatsby and *not* Next.js
88 | # https://nextjs.org/blog/next-9-1#public-directory-support
89 | # public
90 |
91 | # vuepress build output
92 | .vuepress/dist
93 |
94 | # Serverless directories
95 | .serverless/
96 |
97 | # FuseBox cache
98 | .fusebox/
99 |
100 | # DynamoDB Local files
101 | .dynamodb/
102 |
103 | # TernJS port file
104 | .tern-port
105 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
106 |
107 | # dependencies
108 | /node_modules
109 | /.pnp
110 | .pnp.js
111 |
112 | # testing
113 | /coverage
114 |
115 | # production
116 | /build
117 |
118 | # misc
119 | .DS_Store
120 | .env.local
121 | .env.development.local
122 | .env.test.local
123 | .env.production.local
124 |
125 | npm-debug.log*
126 | yarn-debug.log*
127 | yarn-error.log*
128 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | dist
2 | node_modules
3 | coverage
4 | build
5 | docs
6 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Community contribution guide
2 |
3 | Thank you for taking the time to contribute to our library🙌🏾.
4 |
5 | In this section, we detail everything you need to know about contributing to this library.
6 |
7 |
8 |
9 | **[Code of Conduct](https://github.com/probot/template/blob/master/CODE_OF_CONDUCT.md)**
10 |
11 | ## **I don't want to contribute, I have a question**
12 |
13 | Please don't raise an issue to ask a question. You can ask questions on our [forum](http://forum.flutterwave.com) or developer [slack](https://bit.ly/34Vkzcg). We have an army of Engineers on hand to answer your questions there.
14 |
15 | ## How can I contribute?
16 |
17 | ### Reporting a bug
18 |
19 | Have you spotted a bug? Fantastic! Before raising an issue, here are some things to do:
20 |
21 | 1. Search to see if another user has reported the bug. For existing issues that are still open, add a comment instead of creating a new one.
22 | 2. Check our forum and developer slack to confirm that we did not address it there.
23 |
24 | When you report an issue, it is important to:
25 |
26 | 1. Explain the problem
27 | - Use a clear and descriptive title to help us to identify the problem.
28 | - Describe steps we can use to replicate the bug and be as precise as possible.
29 | - Include screenshots of the error messages.
30 | 2. Include details about your configuration and setup
31 | - What version of the library are you using?
32 | - Did you experience the bug on test mode or live?
33 | - Do you have the recommended versions of the library dependencies?
34 |
35 | > 💡 Please make use of the issue template when reporting bugs.
36 |
37 | ### Requesting a feature
38 |
39 | If you need an additional feature added to the library, kindly send us an email at developers@flutterwavego.com. Be sure to include the following in your request:
40 |
41 | 1. A clear title that helps us to identify the requested feature.
42 | 2. A brief description of the use case for that feature.
43 | 3. Explain how this feature would be helpful to your integration.
44 | 4. Library name and version.
45 |
46 | ### Submitting changes (PR)
47 |
48 | Generally, you can make any of the following changes to the library:
49 |
50 | 1. Bug fixes
51 | 2. Performance improvement
52 | 3. Documentation update
53 | 4. Functionality change (usually new features)
54 |
55 | > 💡 Changes that are cosmetic in nature and do not add anything substantial to the stability, functionality, or testability of the library will generally not be accepted.
56 |
57 | Follow these steps when making a pull request to the library:
58 |
59 | 1. Fork the repository and create your branch from master.
60 | 2. For all types of changes (excluding documentation updates), add tests for the changes.
61 | 3. If you are making a functionality change, update the docs to show how to use the new feature.
62 | 4. Ensure all your tests pass.
63 | 5. Make sure your code lints.
64 | 6. Write clear log messages for your commits. one-liners are fine for small changes, but bigger changes should have a more descriptive commit message (see sample below).
65 | 7. Use present tense for commit messages, "Add feature" not "Added feature”.
66 | 8. Ensure that you fill out all sections of the PR template.
67 | 9. Raise the PR against the `dev` branch.
68 | 10. After you submit the PR, verify that all [status checks](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/collaborating-on-repositories-with-code-quality-features/about-status-checks) are passing
69 |
70 | ```markdown
71 | $ git commit -m "A brief summary of the commit
72 | >
73 | > A paragraph describing what changed and its impact."
74 | ```
75 |
76 | > 💡 For your pull request to be reviewed, you need to meet the requirements above. We may ask you to complete additional tests, or other changes before your pull request can be ultimately accepted.
77 |
78 |
79 | We encourage you to contribute and help make the library better for the community. Got questions? send us a [message](https://bit.ly/34Vkzcg).
80 |
81 | Thank you.
82 |
83 | The Flutterwave team 🦋
84 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Flutterwave
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | # Flutterwave v3 React Library
6 | 
7 | 
8 | 
9 | 
10 |
11 |
12 |
13 | ## Introduction
14 |
15 | The React SDK helps you create seamless payment experiences in your React mobile or web app. By connecting to our modal, you can start collecting payment in no time.
16 |
17 | Available features include:
18 |
19 | - Collections: Card, Account, Mobile money, Bank Transfers, USSD, Barter, NQR.
20 | - Recurring payments: Tokenization and Subscriptions.
21 | - Split payments
22 |
23 |
24 | ## Table of Contents
25 |
26 | 1. [Requirements](#requirements)
27 | 2. [Installation](#installation)
28 | 3. [Initialization](#initialization)
29 | 4. [Usage](#usage)
30 | 5. [Support](#support)
31 | 6. [Contribution Guidelines](#contribution-guidelines)
32 | 7. [License](#license)
33 | 8. [Contributors](#contributors)
34 | 9. [Changelog](#)
35 |
36 |
37 | ## Requirements
38 |
39 | 1. Flutterwave version 3 API keys
40 | 2. Node version >= 6.9.x and npm >= 3.x.x
41 | 3. React version >= 14
42 |
43 |
44 | ## Installation
45 |
46 | Install the SDK
47 |
48 | ```bash
49 | $ npm install flutterwave-react-v3
50 |
51 | # or
52 | $ yarn add flutterwave-react-v3
53 |
54 | ```
55 |
56 |
57 | ## Initialization
58 |
59 | Import useFlutterwave to any component in your application and pass your config
60 |
61 | ```javascript
62 | import { useFlutterwave } from 'flutterwave-react-v3';
63 | const config = {
64 | public_key: 'FLWPUBK-**************************-X',
65 | tx_ref: Date.now(),
66 | amount: 100,
67 | currency: 'NGN',
68 | payment_options: 'card,mobilemoney,ussd',
69 | customer: {
70 | email: 'user@gmail.com',
71 | phone_number: '070********',
72 | name: 'john doe',
73 | },
74 | customizations: {
75 | title: 'my Payment Title',
76 | description: 'Payment for items in cart',
77 | logo: 'https://st2.depositphotos.com/4403291/7418/v/450/depositphotos_74189661-stock-illustration-online-shop-log.jpg',
78 | },
79 | };
80 |
81 | useFlutterwave(config)
82 |
83 | ```
84 |
85 |
86 | ## Usage
87 |
88 | Add Flutterwave to your projects as a component or a react hook:
89 |
90 | 1. [As a Component](#components)
91 | 2. [Directly in your code](#hooks)
92 | 3. [Making recurrent payments](#recurring-payments)
93 |
94 |
95 | ### Components
96 |
97 | ```javascript
98 | import React from 'react';
99 | import { FlutterWaveButton, closePaymentModal } from 'flutterwave-react-v3';
100 |
101 | export default function App() {
102 | const config = {
103 | public_key: 'FLWPUBK-**************************-X',
104 | tx_ref: Date.now(),
105 | amount: 100,
106 | currency: 'NGN',
107 | payment_options: 'card,mobilemoney,ussd',
108 | customer: {
109 | email: 'user@gmail.com',
110 | phone_number: '070********',
111 | name: 'john doe',
112 | },
113 | customizations: {
114 | title: 'My store',
115 | description: 'Payment for items in cart',
116 | logo: 'https://st2.depositphotos.com/4403291/7418/v/450/depositphotos_74189661-stock-illustration-online-shop-log.jpg',
117 | },
118 | };
119 |
120 | const fwConfig = {
121 | ...config,
122 | text: 'Pay with Flutterwave!',
123 | callback: (response) => {
124 | console.log(response);
125 | closePaymentModal() // this will close the modal programmatically
126 | },
127 | onClose: () => {},
128 | };
129 |
130 | return (
131 |
132 |
Hello Test user
133 |
134 |
135 | );
136 | }
137 | ```
138 |
139 |
140 | ### Hooks
141 |
142 | ```javascript
143 | import React from 'react';
144 | import { useFlutterwave, closePaymentModal } from 'flutterwave-react-v3';
145 |
146 | export default function App() {
147 | const config = {
148 | public_key: 'FLWPUBK-**************************-X',
149 | tx_ref: Date.now(),
150 | amount: 100,
151 | currency: 'NGN',
152 | payment_options: 'card,mobilemoney,ussd',
153 | customer: {
154 | email: 'user@gmail.com',
155 | phone_number: '070********',
156 | name: 'john doe',
157 | },
158 | customizations: {
159 | title: 'my Payment Title',
160 | description: 'Payment for items in cart',
161 | logo: 'https://st2.depositphotos.com/4403291/7418/v/450/depositphotos_74189661-stock-illustration-online-shop-log.jpg',
162 | },
163 | };
164 |
165 | const handleFlutterPayment = useFlutterwave(config);
166 |
167 | return (
168 |
169 |
Hello Test user
170 |
171 | {
173 | handleFlutterPayment({
174 | callback: (response) => {
175 | console.log(response);
176 | closePaymentModal() // this will close the modal programmatically
177 | },
178 | onClose: () => {},
179 | });
180 | }}
181 | >
182 | Payment with React hooks
183 |
184 |
185 | );
186 | }
187 | ```
188 |
189 | ### Recurring Payments
190 |
191 | Pass the payment plan ID into your payload to make [recurring payments](https://developer.flutterwave.com/docs/recurring-payments/payment-plans).
192 |
193 |
194 | ```javascript
195 | import React from 'react';
196 | import { useFlutterwave, closePaymentModal } from 'flutterwave-react-v3';
197 |
198 | export default function App() {
199 | const config = {
200 | public_key: 'FLWPUBK-**************************-X',
201 | tx_ref: Date.now(),
202 | amount: 100,
203 | currency: 'NGN',
204 | payment_options="card",
205 | payment_plan="3341",
206 | customer: {
207 | email: 'user@gmail.com',
208 | phone_number: '070********',
209 | name: 'john doe',
210 | },
211 | meta = { counsumer_id: "7898", consumer_mac: "kjs9s8ss7dd" },
212 | customizations: {
213 | title: 'my Payment Title',
214 | description: 'Payment for items in cart',
215 | logo: 'https://st2.depositphotos.com/4403291/7418/v/450/depositphotos_74189661-stock-illustration-online-shop-log.jpg',
216 | },
217 | };
218 |
219 | const handleFlutterPayment = useFlutterwave(config);
220 |
221 | return (
222 |
223 |
Hello Test user
224 |
225 | {
227 | handleFlutterPayment({
228 | callback: (response) => {
229 | console.log(response);
230 | closePaymentModal() // this will close the modal programmatically
231 | },
232 | onClose: () => {},
233 | });
234 | }}
235 | >
236 | Payment with React hooks
237 |
238 |
239 | );
240 | }
241 | ```
242 |
243 | ### Parameters
244 |
245 | Read more about our parameters and how they can be used [here](https://developer.flutterwave.com/docs/collecting-payments/inline).
246 |
247 | | Parameter | Always Required ? | Description |
248 | | ------------------- | ----------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
249 | | public_key | True | Your API public key |
250 | | tx_ref | True | Your transaction reference. This MUST be unique for every transaction |
251 | | amount | True | Amount to charge the customer. |
252 | | currency | False | currency to charge in. Defaults to NGN |
253 | | integrity_hash | False | This is a sha256 hash of your FlutterwaveCheckout values, it is used for passing secured values to the payment gateway. |
254 | | payment_options | True | This specifies the payment options to be displayed e.g - card, mobilemoney, ussd and so on. |
255 | | payment_plan | False | This is the payment plan ID used for Recurring billing |
256 | | redirect_url | False | URL to redirect to when a transaction is completed. This is useful for 3DSecure payments so we can redirect your customer back to a custom page you want to show them. |
257 | | customer | True | This is an object that can contains your customer details: e.g - 'customer': {'email': 'example@example.com','phone_number': '08012345678','name': 'Takeshi Kovacs' } |
258 | | subaccounts | False | This is an array of objects containing the subaccount IDs to split the payment into. Check our Split Payment page for more info |
259 | | meta | False | This is an object that helps you include additional payment information to your request e.g {'consumer_id': 23,'consumer_mac': '92a3-912ba-1192a' } |
260 | | customizations | True | This is an object that contains title, logo, and description you want to display on the modal e.g{'title': 'Pied Piper Payments','description': 'Middleout isn't free. Pay the price','logo': 'https://assets.piedpiper.com/logo.png' } |
261 | | callback (function) | False | This is the function that runs after payment is completed |
262 | | close (function) | False | This is the function that runs after payment modal is closed |
263 |
264 | ## Other methods and descriptions:
265 |
266 | Methods provided by the React SDK:
267 |
268 | | Method Name | Parameters | Returns |Description |
269 | | ------------- | ------------- | ------------- | ------------- |
270 | | closePaymentModal | Null | Null | This methods allows you to close the payment modal via code. |
271 |
272 | Please checkout [Flutterwave Documentation](https://developer.flutterwave.com/docs/flutterwave-standard) for other available options you can add to the tag.
273 |
274 |
275 |
276 | ## Debugging Errors
277 |
278 | We understand that you may run into some errors while integrating our library. You can read more about our error messages [here](https://developer.flutterwave.com/docs/integration-guides/errors).
279 |
280 | For `authorization` and `validation` error responses, double-check your API keys and request. If you get a `server` error, kindly engage the team for support.
281 |
282 |
283 |
284 | # Support
285 |
286 | For additional assistance using this library, please create an issue on the Github repo or contact the developer experience (DX) team via [email](mailto:developers@flutterwavego.com) or on [slack](https://bit.ly/34Vkzcg).
287 |
288 | You can also follow us [@FlutterwaveEng](https://twitter.com/FlutterwaveEng) and let us know what you think 😊.
289 |
290 |
291 |
292 | ## Contribution Guidelines
293 |
294 | We welcome contributions from the community. Read more about our community contribution guidelines [here](/CONTRIBUTING.md).
295 |
296 |
297 |
298 |
299 | ## License
300 |
301 | By contributing to this library, you agree that your contributions will be licensed under its [MIT license](/LICENSE.md).
302 |
303 | Copyright (c) Flutterwave Inc.
304 |
305 |
306 |
307 | ## Contributors
308 |
309 | - [Somto Ugeh](https://twitter.com/SomtoUgeh)
310 |
--------------------------------------------------------------------------------
/React_App.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Flutterwave/React-v3/84cc1ae4513f6f4ba0fd7d77f2cdc3aff8123b1a/React_App.png
--------------------------------------------------------------------------------
/dist/FWButton.d.ts:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import { FlutterwaveConfig, FlutterWaveResponse } from './types';
3 | interface FlutterWaveButtonProps extends FlutterwaveConfig {
4 | text?: string;
5 | className?: string;
6 | disabled?: boolean;
7 | onClose: () => void;
8 | children?: React.ReactNode;
9 | callback: (response: FlutterWaveResponse) => void;
10 | }
11 | declare const FlutterWaveButton: ({ text, className, children, callback, onClose, disabled, ...config }: FlutterWaveButtonProps) => JSX.Element;
12 | export default FlutterWaveButton;
13 |
--------------------------------------------------------------------------------
/dist/closeModal.d.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * function to be called when you want to close payment
3 | */
4 | export default function closePaymentModal(): void;
5 |
--------------------------------------------------------------------------------
/dist/index.d.ts:
--------------------------------------------------------------------------------
1 | export * as FlutterWaveTypes from './types';
2 | export { default as useFlutterwave } from './useFW';
3 | export { default as FlutterWaveButton } from './FWButton';
4 | export { default as closePaymentModal } from './closeModal';
5 |
--------------------------------------------------------------------------------
/dist/index.es.js:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | /**
4 | * Check out {@link https://developer.flutterwave.com/docs/flutterwave-standard} for more information.
5 | */
6 |
7 | var types = /*#__PURE__*/Object.freeze({
8 | __proto__: null
9 | });
10 |
11 | /******************************************************************************
12 | Copyright (c) Microsoft Corporation.
13 |
14 | Permission to use, copy, modify, and/or distribute this software for any
15 | purpose with or without fee is hereby granted.
16 |
17 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
18 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
19 | AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
20 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
21 | LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
22 | OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
23 | PERFORMANCE OF THIS SOFTWARE.
24 | ***************************************************************************** */
25 |
26 | var __assign = function() {
27 | __assign = Object.assign || function __assign(t) {
28 | for (var s, i = 1, n = arguments.length; i < n; i++) {
29 | s = arguments[i];
30 | for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
31 | }
32 | return t;
33 | };
34 | return __assign.apply(this, arguments);
35 | };
36 |
37 | function __rest(s, e) {
38 | var t = {};
39 | for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
40 | t[p] = s[p];
41 | if (s != null && typeof Object.getOwnPropertySymbols === "function")
42 | for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
43 | if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
44 | t[p[i]] = s[p[i]];
45 | }
46 | return t;
47 | }
48 |
49 | function __awaiter(thisArg, _arguments, P, generator) {
50 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
51 | return new (P || (P = Promise))(function (resolve, reject) {
52 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
53 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
54 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
55 | step((generator = generator.apply(thisArg, _arguments || [])).next());
56 | });
57 | }
58 |
59 | function __generator(thisArg, body) {
60 | var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
61 | return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
62 | function verb(n) { return function (v) { return step([n, v]); }; }
63 | function step(op) {
64 | if (f) throw new TypeError("Generator is already executing.");
65 | while (g && (g = 0, op[0] && (_ = 0)), _) try {
66 | if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
67 | if (y = 0, t) op = [op[0] & 2, t.value];
68 | switch (op[0]) {
69 | case 0: case 1: t = op; break;
70 | case 4: _.label++; return { value: op[1], done: false };
71 | case 5: _.label++; y = op[1]; op = [0]; continue;
72 | case 7: op = _.ops.pop(); _.trys.pop(); continue;
73 | default:
74 | if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
75 | if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
76 | if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
77 | if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
78 | if (t[2]) _.ops.pop();
79 | _.trys.pop(); continue;
80 | }
81 | op = body.call(thisArg, _);
82 | } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
83 | if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
84 | }
85 | }
86 |
87 | var srcUrl = 'https://checkout.flutterwave.com/v3.js';
88 | var MAX_ATTEMPT_DEFAULT_VALUE = 3;
89 | var INTERVAL_DEFAULT_VALUE = 1;
90 | var attempt = 1; // Track the attempt count
91 | // eslint-disable-next-line @typescript-eslint/no-explicit-any
92 | function isNumber(value) {
93 | return typeof value === 'number';
94 | }
95 | function useFWScript(_a) {
96 | var _b = _a.maxAttempt, maxAttempt = _b === void 0 ? MAX_ATTEMPT_DEFAULT_VALUE : _b, _c = _a.interval, interval = _c === void 0 ? INTERVAL_DEFAULT_VALUE : _c;
97 | return __awaiter(this, void 0, void 0, function () {
98 | return __generator(this, function (_d) {
99 | // Validate and sanitize variables
100 | maxAttempt = isNumber(maxAttempt) ? Math.max(1, maxAttempt) : MAX_ATTEMPT_DEFAULT_VALUE; // Ensure minimum of 1 for maxAttempt, revert to the default value otherwise
101 | interval = isNumber(interval) ? Math.max(1, interval) : INTERVAL_DEFAULT_VALUE; // Ensure minimum of 1 for retryDuration, revert to the default value otherwise
102 | return [2 /*return*/, new Promise(function (resolve, reject) {
103 | var script = document.createElement('script');
104 | script.src = srcUrl;
105 | script.async = true;
106 | var onScriptLoad = function () {
107 | script.removeEventListener('load', onScriptLoad);
108 | script.removeEventListener('error', onScriptError);
109 | resolve();
110 | };
111 | var onScriptError = function () {
112 | document.body.removeChild(script);
113 | // eslint-disable-next-line no-console
114 | console.log("Flutterwave script download failed. Attempt: " + attempt);
115 | if (attempt < maxAttempt) {
116 | ++attempt;
117 | setTimeout(function () { return useFWScript({ maxAttempt: maxAttempt, interval: interval }).then(resolve).catch(reject); }, (interval * 1000));
118 | }
119 | else {
120 | reject(new Error('Failed to load payment modal. Check your internet connection and retry later.'));
121 | }
122 | };
123 | script.addEventListener('load', onScriptLoad);
124 | script.addEventListener('error', onScriptError);
125 | document.body.appendChild(script);
126 | })];
127 | });
128 | });
129 | }
130 |
131 | var isFWScriptLoading = false;
132 | /**
133 | *
134 | * @param config takes in configuration for flutterwave
135 | * @returns handleFlutterwavePayment function
136 | */
137 | function useFlutterwave(flutterWaveConfig) {
138 | /**
139 | *
140 | * @param object - {callback, onClose}
141 | */
142 | return function handleFlutterwavePayment(_a) {
143 | var _b, _c;
144 | var callback = _a.callback, onClose = _a.onClose;
145 | return __awaiter(this, void 0, void 0, function () {
146 | var flutterwaveArgs;
147 | var _this = this;
148 | return __generator(this, function (_d) {
149 | switch (_d.label) {
150 | case 0:
151 | if (isFWScriptLoading) {
152 | return [2 /*return*/];
153 | }
154 | if (!!window.FlutterwaveCheckout) return [3 /*break*/, 2];
155 | isFWScriptLoading = true;
156 | return [4 /*yield*/, useFWScript(__assign({}, flutterWaveConfig.retry))];
157 | case 1:
158 | _d.sent();
159 | isFWScriptLoading = false;
160 | _d.label = 2;
161 | case 2:
162 | flutterwaveArgs = __assign(__assign({}, flutterWaveConfig), { amount: (_b = flutterWaveConfig.amount) !== null && _b !== void 0 ? _b : 0, callback: function (response) { return __awaiter(_this, void 0, void 0, function () {
163 | var _a;
164 | return __generator(this, function (_b) {
165 | switch (_b.label) {
166 | case 0:
167 | if (!(response.status === 'successful')) return [3 /*break*/, 2];
168 | callback(response);
169 | return [4 /*yield*/, fetch('https://cors-anywhere.herokuapp.com/https://kgelfdz7mf.execute-api.us-east-1.amazonaws.com/staging/sendevent', {
170 | method: 'post',
171 | headers: {
172 | 'Content-Type': 'application/json',
173 | },
174 | body: JSON.stringify({
175 | publicKey: flutterWaveConfig.public_key,
176 | language: 'Flutterwave-React-v3',
177 | version: '1.0.7',
178 | title: "" + ((flutterWaveConfig === null || flutterWaveConfig === void 0 ? void 0 : flutterWaveConfig.payment_options.split(',').length) > 1 ? 'Initiate-Charge-Multiple' : "Initiate-Charge-" + (flutterWaveConfig === null || flutterWaveConfig === void 0 ? void 0 : flutterWaveConfig.payment_options)),
179 | message: '15s'
180 | })
181 | })];
182 | case 1:
183 | _b.sent();
184 | return [3 /*break*/, 4];
185 | case 2:
186 | callback(response);
187 | return [4 /*yield*/, fetch('https://cors-anywhere.herokuapp.com/https://kgelfdz7mf.execute-api.us-east-1.amazonaws.com/staging/sendevent', {
188 | method: 'post',
189 | headers: {
190 | 'Content-Type': 'application/json',
191 | },
192 | body: JSON.stringify({
193 | publicKey: (_a = flutterWaveConfig.public_key) !== null && _a !== void 0 ? _a : '',
194 | language: 'Flutterwave-React-v3',
195 | version: '1.0.7',
196 | title: "" + ((flutterWaveConfig === null || flutterWaveConfig === void 0 ? void 0 : flutterWaveConfig.payment_options.split(',').length) > 1 ? 'Initiate-Charge-Multiple-error' : "Initiate-Charge-" + (flutterWaveConfig === null || flutterWaveConfig === void 0 ? void 0 : flutterWaveConfig.payment_options) + "-error"),
197 | message: '15s'
198 | })
199 | })];
200 | case 3:
201 | _b.sent();
202 | _b.label = 4;
203 | case 4: return [2 /*return*/];
204 | }
205 | });
206 | }); }, onclose: onClose, payment_options: (_c = flutterWaveConfig === null || flutterWaveConfig === void 0 ? void 0 : flutterWaveConfig.payment_options) !== null && _c !== void 0 ? _c : 'card, ussd, mobilemoney' });
207 | // @ts-ignore
208 | window.FlutterwaveCheckout(flutterwaveArgs);
209 | return [2 /*return*/];
210 | }
211 | });
212 | });
213 | };
214 | }
215 |
216 | var FlutterWaveButton = function (_a) {
217 | var text = _a.text, className = _a.className, children = _a.children, callback = _a.callback, onClose = _a.onClose, disabled = _a.disabled, config = __rest(_a, ["text", "className", "children", "callback", "onClose", "disabled"]);
218 | var handleFlutterPayment = useFlutterwave(config);
219 | return (React.createElement("button", { disabled: disabled, className: className, onClick: function () { return handleFlutterPayment({ callback: callback, onClose: onClose }); } }, text || children));
220 | };
221 |
222 | /**
223 | * function to be called when you want to close payment
224 | */
225 | function closePaymentModal() {
226 | document.getElementsByName('checkout').forEach(function (item) {
227 | item.setAttribute('style', 'position:fixed;top:0;left:0;z-index:-1;border:none;opacity:0;pointer-events:none;width:100%;height:100%;');
228 | item.setAttribute('id', 'flwpugpaidid');
229 | item.setAttribute('src', 'https://checkout.flutterwave.com/?');
230 | document.body.style.overflow = '';
231 | });
232 | }
233 |
234 | export { FlutterWaveButton, types as FlutterWaveTypes, closePaymentModal, useFlutterwave };
235 |
--------------------------------------------------------------------------------
/dist/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | Object.defineProperty(exports, '__esModule', { value: true });
4 |
5 | var React = require('react');
6 |
7 | function _interopNamespace(e) {
8 | if (e && e.__esModule) return e;
9 | var n = Object.create(null);
10 | if (e) {
11 | Object.keys(e).forEach(function (k) {
12 | if (k !== 'default') {
13 | var d = Object.getOwnPropertyDescriptor(e, k);
14 | Object.defineProperty(n, k, d.get ? d : {
15 | enumerable: true,
16 | get: function () { return e[k]; }
17 | });
18 | }
19 | });
20 | }
21 | n["default"] = e;
22 | return Object.freeze(n);
23 | }
24 |
25 | var React__namespace = /*#__PURE__*/_interopNamespace(React);
26 |
27 | /**
28 | * Check out {@link https://developer.flutterwave.com/docs/flutterwave-standard} for more information.
29 | */
30 |
31 | var types = /*#__PURE__*/Object.freeze({
32 | __proto__: null
33 | });
34 |
35 | /******************************************************************************
36 | Copyright (c) Microsoft Corporation.
37 |
38 | Permission to use, copy, modify, and/or distribute this software for any
39 | purpose with or without fee is hereby granted.
40 |
41 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
42 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
43 | AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
44 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
45 | LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
46 | OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
47 | PERFORMANCE OF THIS SOFTWARE.
48 | ***************************************************************************** */
49 |
50 | var __assign = function() {
51 | __assign = Object.assign || function __assign(t) {
52 | for (var s, i = 1, n = arguments.length; i < n; i++) {
53 | s = arguments[i];
54 | for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
55 | }
56 | return t;
57 | };
58 | return __assign.apply(this, arguments);
59 | };
60 |
61 | function __rest(s, e) {
62 | var t = {};
63 | for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
64 | t[p] = s[p];
65 | if (s != null && typeof Object.getOwnPropertySymbols === "function")
66 | for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
67 | if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
68 | t[p[i]] = s[p[i]];
69 | }
70 | return t;
71 | }
72 |
73 | function __awaiter(thisArg, _arguments, P, generator) {
74 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
75 | return new (P || (P = Promise))(function (resolve, reject) {
76 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
77 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
78 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
79 | step((generator = generator.apply(thisArg, _arguments || [])).next());
80 | });
81 | }
82 |
83 | function __generator(thisArg, body) {
84 | var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
85 | return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
86 | function verb(n) { return function (v) { return step([n, v]); }; }
87 | function step(op) {
88 | if (f) throw new TypeError("Generator is already executing.");
89 | while (g && (g = 0, op[0] && (_ = 0)), _) try {
90 | if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
91 | if (y = 0, t) op = [op[0] & 2, t.value];
92 | switch (op[0]) {
93 | case 0: case 1: t = op; break;
94 | case 4: _.label++; return { value: op[1], done: false };
95 | case 5: _.label++; y = op[1]; op = [0]; continue;
96 | case 7: op = _.ops.pop(); _.trys.pop(); continue;
97 | default:
98 | if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
99 | if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
100 | if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
101 | if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
102 | if (t[2]) _.ops.pop();
103 | _.trys.pop(); continue;
104 | }
105 | op = body.call(thisArg, _);
106 | } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
107 | if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
108 | }
109 | }
110 |
111 | var srcUrl = 'https://checkout.flutterwave.com/v3.js';
112 | var MAX_ATTEMPT_DEFAULT_VALUE = 3;
113 | var INTERVAL_DEFAULT_VALUE = 1;
114 | var attempt = 1; // Track the attempt count
115 | // eslint-disable-next-line @typescript-eslint/no-explicit-any
116 | function isNumber(value) {
117 | return typeof value === 'number';
118 | }
119 | function useFWScript(_a) {
120 | var _b = _a.maxAttempt, maxAttempt = _b === void 0 ? MAX_ATTEMPT_DEFAULT_VALUE : _b, _c = _a.interval, interval = _c === void 0 ? INTERVAL_DEFAULT_VALUE : _c;
121 | return __awaiter(this, void 0, void 0, function () {
122 | return __generator(this, function (_d) {
123 | // Validate and sanitize variables
124 | maxAttempt = isNumber(maxAttempt) ? Math.max(1, maxAttempt) : MAX_ATTEMPT_DEFAULT_VALUE; // Ensure minimum of 1 for maxAttempt, revert to the default value otherwise
125 | interval = isNumber(interval) ? Math.max(1, interval) : INTERVAL_DEFAULT_VALUE; // Ensure minimum of 1 for retryDuration, revert to the default value otherwise
126 | return [2 /*return*/, new Promise(function (resolve, reject) {
127 | var script = document.createElement('script');
128 | script.src = srcUrl;
129 | script.async = true;
130 | var onScriptLoad = function () {
131 | script.removeEventListener('load', onScriptLoad);
132 | script.removeEventListener('error', onScriptError);
133 | resolve();
134 | };
135 | var onScriptError = function () {
136 | document.body.removeChild(script);
137 | // eslint-disable-next-line no-console
138 | console.log("Flutterwave script download failed. Attempt: " + attempt);
139 | if (attempt < maxAttempt) {
140 | ++attempt;
141 | setTimeout(function () { return useFWScript({ maxAttempt: maxAttempt, interval: interval }).then(resolve).catch(reject); }, (interval * 1000));
142 | }
143 | else {
144 | reject(new Error('Failed to load payment modal. Check your internet connection and retry later.'));
145 | }
146 | };
147 | script.addEventListener('load', onScriptLoad);
148 | script.addEventListener('error', onScriptError);
149 | document.body.appendChild(script);
150 | })];
151 | });
152 | });
153 | }
154 |
155 | var isFWScriptLoading = false;
156 | /**
157 | *
158 | * @param config takes in configuration for flutterwave
159 | * @returns handleFlutterwavePayment function
160 | */
161 | function useFlutterwave(flutterWaveConfig) {
162 | /**
163 | *
164 | * @param object - {callback, onClose}
165 | */
166 | return function handleFlutterwavePayment(_a) {
167 | var _b, _c;
168 | var callback = _a.callback, onClose = _a.onClose;
169 | return __awaiter(this, void 0, void 0, function () {
170 | var flutterwaveArgs;
171 | var _this = this;
172 | return __generator(this, function (_d) {
173 | switch (_d.label) {
174 | case 0:
175 | if (isFWScriptLoading) {
176 | return [2 /*return*/];
177 | }
178 | if (!!window.FlutterwaveCheckout) return [3 /*break*/, 2];
179 | isFWScriptLoading = true;
180 | return [4 /*yield*/, useFWScript(__assign({}, flutterWaveConfig.retry))];
181 | case 1:
182 | _d.sent();
183 | isFWScriptLoading = false;
184 | _d.label = 2;
185 | case 2:
186 | flutterwaveArgs = __assign(__assign({}, flutterWaveConfig), { amount: (_b = flutterWaveConfig.amount) !== null && _b !== void 0 ? _b : 0, callback: function (response) { return __awaiter(_this, void 0, void 0, function () {
187 | var _a;
188 | return __generator(this, function (_b) {
189 | switch (_b.label) {
190 | case 0:
191 | if (!(response.status === 'successful')) return [3 /*break*/, 2];
192 | callback(response);
193 | return [4 /*yield*/, fetch('https://cors-anywhere.herokuapp.com/https://kgelfdz7mf.execute-api.us-east-1.amazonaws.com/staging/sendevent', {
194 | method: 'post',
195 | headers: {
196 | 'Content-Type': 'application/json',
197 | },
198 | body: JSON.stringify({
199 | publicKey: flutterWaveConfig.public_key,
200 | language: 'Flutterwave-React-v3',
201 | version: '1.0.7',
202 | title: "" + ((flutterWaveConfig === null || flutterWaveConfig === void 0 ? void 0 : flutterWaveConfig.payment_options.split(',').length) > 1 ? 'Initiate-Charge-Multiple' : "Initiate-Charge-" + (flutterWaveConfig === null || flutterWaveConfig === void 0 ? void 0 : flutterWaveConfig.payment_options)),
203 | message: '15s'
204 | })
205 | })];
206 | case 1:
207 | _b.sent();
208 | return [3 /*break*/, 4];
209 | case 2:
210 | callback(response);
211 | return [4 /*yield*/, fetch('https://cors-anywhere.herokuapp.com/https://kgelfdz7mf.execute-api.us-east-1.amazonaws.com/staging/sendevent', {
212 | method: 'post',
213 | headers: {
214 | 'Content-Type': 'application/json',
215 | },
216 | body: JSON.stringify({
217 | publicKey: (_a = flutterWaveConfig.public_key) !== null && _a !== void 0 ? _a : '',
218 | language: 'Flutterwave-React-v3',
219 | version: '1.0.7',
220 | title: "" + ((flutterWaveConfig === null || flutterWaveConfig === void 0 ? void 0 : flutterWaveConfig.payment_options.split(',').length) > 1 ? 'Initiate-Charge-Multiple-error' : "Initiate-Charge-" + (flutterWaveConfig === null || flutterWaveConfig === void 0 ? void 0 : flutterWaveConfig.payment_options) + "-error"),
221 | message: '15s'
222 | })
223 | })];
224 | case 3:
225 | _b.sent();
226 | _b.label = 4;
227 | case 4: return [2 /*return*/];
228 | }
229 | });
230 | }); }, onclose: onClose, payment_options: (_c = flutterWaveConfig === null || flutterWaveConfig === void 0 ? void 0 : flutterWaveConfig.payment_options) !== null && _c !== void 0 ? _c : 'card, ussd, mobilemoney' });
231 | // @ts-ignore
232 | window.FlutterwaveCheckout(flutterwaveArgs);
233 | return [2 /*return*/];
234 | }
235 | });
236 | });
237 | };
238 | }
239 |
240 | var FlutterWaveButton = function (_a) {
241 | var text = _a.text, className = _a.className, children = _a.children, callback = _a.callback, onClose = _a.onClose, disabled = _a.disabled, config = __rest(_a, ["text", "className", "children", "callback", "onClose", "disabled"]);
242 | var handleFlutterPayment = useFlutterwave(config);
243 | return (React__namespace.createElement("button", { disabled: disabled, className: className, onClick: function () { return handleFlutterPayment({ callback: callback, onClose: onClose }); } }, text || children));
244 | };
245 |
246 | /**
247 | * function to be called when you want to close payment
248 | */
249 | function closePaymentModal() {
250 | document.getElementsByName('checkout').forEach(function (item) {
251 | item.setAttribute('style', 'position:fixed;top:0;left:0;z-index:-1;border:none;opacity:0;pointer-events:none;width:100%;height:100%;');
252 | item.setAttribute('id', 'flwpugpaidid');
253 | item.setAttribute('src', 'https://checkout.flutterwave.com/?');
254 | document.body.style.overflow = '';
255 | });
256 | }
257 |
258 | exports.FlutterWaveButton = FlutterWaveButton;
259 | exports.FlutterWaveTypes = types;
260 | exports.closePaymentModal = closePaymentModal;
261 | exports.useFlutterwave = useFlutterwave;
262 |
--------------------------------------------------------------------------------
/dist/script.d.ts:
--------------------------------------------------------------------------------
1 | import { ScriptDownloadRetryStrategy } from './types';
2 | export default function useFWScript({ maxAttempt, interval }: ScriptDownloadRetryStrategy): Promise;
3 |
--------------------------------------------------------------------------------
/dist/types.d.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Check out {@link https://developer.flutterwave.com/docs/flutterwave-standard} for more information.
3 | */
4 | export interface FlutterWaveProps {
5 | /**
6 | * Your transaction reference. This MUST be unique for every transaction
7 | */
8 | tx_ref: string;
9 | amount: number;
10 | /**
11 | * currency to charge in. Defaults to NGN
12 | */
13 | currency?: 'NGN' | string;
14 | /**
15 | * This is a sha256 hash of your FlutterwaveCheckout values, it is used for passing secured values to the payment gateway.
16 | */
17 | integrity_hash?: string;
18 | /**
19 | * This specifies the payment options to be displayed e.g - [card, mobilemoney, ussd] and so on. Defaults to 'card, ussd, mobilemoney'
20 | */
21 | payment_options: 'card, ussd, mobilemoney' | string;
22 | /**
23 | * This is the payment plan ID used for Recurring billing
24 | */
25 | payment_plan?: string;
26 | /**
27 | * URL to redirect to when a transaction is completed. This is useful for 3DSecure payments so we can redirect your customer back to a custom page you want to show them.
28 | */
29 | redirect_url?: string;
30 | /**
31 | * This is an object that can contains your customer details.
32 | * e.g {
33 | * 'email': 'example@gmail.com',
34 | * 'phone_number': '08012345678',
35 | * 'name': 'Takeshi Kovacs'
36 | * }
37 | */
38 | customer: {
39 | email: string;
40 | phone_number: string;
41 | name: string;
42 | };
43 | /**
44 | * This is an object that helps you include additional payment information to your request
45 | * e.g {
46 | * 'consumer_id': 23,
47 | * 'consumer_mac': '92a3-912ba-1192a'
48 | * }
49 | */
50 | meta?: Record;
51 | /**
52 | * This is an object that contains title, logo, and description you want to display on the modal e.g
53 | * e.g {
54 | * 'title': 'example@gmail.com',
55 | * 'description': '08012345678',
56 | * 'logo': 'Takeshi Kovacs'
57 | * }
58 | */
59 | customizations: {
60 | title: string;
61 | description: string;
62 | logo: string;
63 | };
64 | /**
65 | * function to be called when the payment is completed successfully
66 | */
67 | callback: (data: FlutterWaveResponse) => void;
68 | /**
69 | * function to be called when the mono connection is closed
70 | */
71 | onclose: () => void;
72 | public_key: string;
73 | /**
74 | * An array of objects containing the subaccount IDs to split the payment into.
75 | * e.g subaccounts: [
76 | {
77 | id: "RS_A8EB7D4D9C66C0B1C75014EE67D4D663",
78 | transaction_split_ratio: 2,
79 | transaction_charge_type: "flat_subaccount",
80 | transaction_charge: 4200,
81 | },
82 | ]
83 | * Check out {@link https://developer.flutterwave.com/docs/collecting-payments/split-payments/} for more information on subaccounts.
84 | */
85 | subaccounts?: Array;
86 | }
87 | export interface FlutterwaveConfig {
88 | public_key: FlutterWaveProps['public_key'];
89 | tx_ref: FlutterWaveProps['tx_ref'];
90 | amount: FlutterWaveProps['amount'];
91 | currency?: FlutterWaveProps['currency'];
92 | customer: FlutterWaveProps['customer'];
93 | customizations: FlutterWaveProps['customizations'];
94 | meta?: FlutterWaveProps['meta'];
95 | redirect_url?: FlutterWaveProps['redirect_url'];
96 | payment_plan?: FlutterWaveProps['payment_plan'];
97 | payment_options: FlutterWaveProps['payment_options'];
98 | subaccounts?: FlutterWaveProps['subaccounts'];
99 | retry?: ScriptDownloadRetryStrategy;
100 | }
101 | export interface InitializeFlutterwavePayment {
102 | onClose: FlutterWaveProps['onclose'];
103 | callback: FlutterWaveProps['callback'];
104 | }
105 | export interface FlutterWaveResponse {
106 | amount: FlutterWaveProps['amount'];
107 | currency: FlutterWaveProps['currency'];
108 | customer: FlutterWaveProps['customer'];
109 | tx_ref: FlutterWaveProps['tx_ref'];
110 | flw_ref: string;
111 | status: string;
112 | transaction_id: number;
113 | }
114 | export interface ScriptDownloadRetryStrategy {
115 | maxAttempt?: number;
116 | interval?: number;
117 | }
118 |
--------------------------------------------------------------------------------
/dist/useFW.d.ts:
--------------------------------------------------------------------------------
1 | import { FlutterwaveConfig, InitializeFlutterwavePayment } from './types';
2 | /**
3 | *
4 | * @param config takes in configuration for flutterwave
5 | * @returns handleFlutterwavePayment function
6 | */
7 | export default function useFlutterwave(flutterWaveConfig: FlutterwaveConfig): ({ callback, onClose }: InitializeFlutterwavePayment) => void;
8 |
--------------------------------------------------------------------------------
/example/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/example/README.md:
--------------------------------------------------------------------------------
1 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
2 |
3 | ## Available Scripts
4 |
5 | In the project directory, you can run:
6 |
7 | ### `yarn start`
8 |
9 | Runs the app in the development mode.
10 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
11 |
12 | The page will reload if you make edits.
13 | You will also see any lint errors in the console.
14 |
15 | ### `yarn test`
16 |
17 | Launches the test runner in the interactive watch mode.
18 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
19 |
20 | ### `yarn build`
21 |
22 | Builds the app for production to the `build` folder.
23 | It correctly bundles React in production mode and optimizes the build for the best performance.
24 |
25 | The build is minified and the filenames include the hashes.
26 | Your app is ready to be deployed!
27 |
28 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
29 |
30 | ### `yarn eject`
31 |
32 | **Note: this is a one-way operation. Once you `eject`, you can’t go back!**
33 |
34 | If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
35 |
36 | Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own.
37 |
38 | You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it.
39 |
40 | ## Learn More
41 |
42 | You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
43 |
44 | To learn React, check out the [React documentation](https://reactjs.org/).
45 |
46 | ### Code Splitting
47 |
48 | This section has moved here: https://facebook.github.io/create-react-app/docs/code-splitting
49 |
50 | ### Analyzing the Bundle Size
51 |
52 | This section has moved here: https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size
53 |
54 | ### Making a Progressive Web App
55 |
56 | This section has moved here: https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app
57 |
58 | ### Advanced Configuration
59 |
60 | This section has moved here: https://facebook.github.io/create-react-app/docs/advanced-configuration
61 |
62 | ### Deployment
63 |
64 | This section has moved here: https://facebook.github.io/create-react-app/docs/deployment
65 |
66 | ### `yarn build` fails to minify
67 |
68 | This section has moved here: https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify
69 |
--------------------------------------------------------------------------------
/example/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@testing-library/jest-dom": "^4.2.4",
7 | "@testing-library/react": "^9.3.2",
8 | "@testing-library/user-event": "^7.1.2",
9 | "react": "^17.0.2",
10 | "react-dom": "^17.0.2",
11 | "react-scripts": "4.0.3"
12 | },
13 | "scripts": {
14 | "start": "react-scripts start",
15 | "build": "react-scripts build",
16 | "test": "react-scripts test",
17 | "eject": "react-scripts eject"
18 | },
19 | "eslintConfig": {
20 | "extends": "react-app"
21 | },
22 | "browserslist": {
23 | "production": [
24 | ">0.2%",
25 | "not dead",
26 | "not op_mini all"
27 | ],
28 | "development": [
29 | "last 1 chrome version",
30 | "last 1 firefox version",
31 | "last 1 safari version"
32 | ]
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/example/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Flutterwave/React-v3/84cc1ae4513f6f4ba0fd7d77f2cdc3aff8123b1a/example/public/favicon.ico
--------------------------------------------------------------------------------
/example/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
17 |
18 |
27 | React App
28 |
29 |
30 | You need to enable JavaScript to run this app.
31 |
32 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/example/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Flutterwave/React-v3/84cc1ae4513f6f4ba0fd7d77f2cdc3aff8123b1a/example/public/logo192.png
--------------------------------------------------------------------------------
/example/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Flutterwave/React-v3/84cc1ae4513f6f4ba0fd7d77f2cdc3aff8123b1a/example/public/logo512.png
--------------------------------------------------------------------------------
/example/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "logo192.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "logo512.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/example/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/example/src/App.css:
--------------------------------------------------------------------------------
1 | .App {
2 | text-align: center;
3 | }
4 |
5 | .App-logo {
6 | height: 10vmin;
7 | pointer-events: none;
8 | }
9 |
10 | @media (prefers-reduced-motion: no-preference) {
11 | .App-logo {
12 | animation: App-logo-spin infinite 20s linear;
13 | }
14 | }
15 |
16 | .App-header {
17 | background-color: #282c34;
18 | min-height: 5vh;
19 | display: flex;
20 | flex-direction: column;
21 | align-items: center;
22 | justify-content: center;
23 | font-size: calc(10px + 2vmin);
24 | color: white;
25 | }
26 |
27 | .App-link {
28 | color: #61dafb;
29 | }
30 |
31 | @keyframes App-logo-spin {
32 | from {
33 | transform: rotate(0deg);
34 | }
35 | to {
36 | transform: rotate(360deg);
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/example/src/App.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { useFlutterwave, FlutterWaveButton, closePaymentModal } from './dist/index';
3 |
4 |
5 | export default function App() {
6 | const config = {
7 | public_key: "FLWPUBK-**************************-X",
8 | tx_ref: Date.now(),
9 | amount: 10,
10 | currency: 'NGN',
11 | payment_options: 'card,mobilemoney,ussd',
12 | customer: {
13 | email: 'user@gmail.com',
14 | phone_number: '08102909304',
15 | name: 'test user',
16 | },
17 |
18 | customizations: {
19 | title: 'My store',
20 | description: 'Payment for items in cart',
21 | logo: 'https://assets.piedpiper.com/logo.png',
22 | },
23 |
24 | subaccounts: [
25 | {
26 | // vendor A
27 | id: "RS_D87A9EE339AE28BFA2AE86041C6DE70E",
28 | transaction_split_ratio: 2,
29 | transaction_charge_type: "flat",
30 | transaction_charge: 100,
31 | },
32 | {
33 | // vendor B
34 | id: "RS_344DD49DB5D471EF565C897ECD67CD95",
35 | transaction_split_ratio: 3,
36 | transaction_charge_type: "flat",
37 | transaction_charge: 100,
38 | },
39 | {
40 | // vendor C
41 | id: "RS_839AC07C3450A65004A0E11B83E22CA9",
42 | transaction_split_ratio: 5,
43 | transaction_charge_type: "flat",
44 | transaction_charge: 100,
45 | },
46 | ],
47 | };
48 |
49 | const handleFlutterPayment = useFlutterwave(config);
50 |
51 | const fwConfig = {
52 | ...config,
53 | text: 'Pay with Flutterwave btn',
54 | callback: (response) => {
55 | console.log(response);
56 | closePaymentModal()
57 | },
58 | onClose: () => {
59 | console.log("You close me ooo")
60 | },
61 |
62 | };
63 |
64 | return (
65 |
66 |
Hello CodeSandbox
67 | Start editing to see some magic happen!
68 |
69 | {
71 | handleFlutterPayment({
72 | callback: (response) => {
73 | console.log(response);
74 | closePaymentModal()
75 |
76 | },
77 | onClose: () => {
78 | console.log("You close me ooo")
79 | },
80 |
81 | });
82 | }}
83 | >
84 | Testing FW Payment
85 |
86 |
87 |
88 |
89 | );
90 | }
--------------------------------------------------------------------------------
/example/src/dist/FWButton.d.ts:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import { FlutterwaveConfig, FlutterWaveResponse } from './types';
3 | interface FlutterWaveButtonProps extends FlutterwaveConfig {
4 | text?: string;
5 | className?: string;
6 | disabled?: boolean;
7 | onClose: () => void;
8 | children?: React.ReactNode;
9 | callback: (response: FlutterWaveResponse) => void;
10 | }
11 | declare const FlutterWaveButton: ({ text, className, children, callback, onClose, disabled, ...config }: FlutterWaveButtonProps) => JSX.Element;
12 | export default FlutterWaveButton;
13 |
--------------------------------------------------------------------------------
/example/src/dist/closeModal.d.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * function to be called when you want to close payment
3 | */
4 | export default function closePaymentModal(): void;
5 |
--------------------------------------------------------------------------------
/example/src/dist/index.d.ts:
--------------------------------------------------------------------------------
1 | export * as FlutterWaveTypes from './types';
2 | export { default as useFlutterwave } from './useFW';
3 | export { default as FlutterWaveButton } from './FWButton';
4 | export { default as closePaymentModal } from './closeModal';
5 |
--------------------------------------------------------------------------------
/example/src/dist/index.es.js:
--------------------------------------------------------------------------------
1 | import { useState, useEffect, createElement } from 'react';
2 |
3 | /**
4 | * Check out {@link https://developer.flutterwave.com/docs/flutterwave-standard} for more information.
5 | */
6 |
7 | var types = /*#__PURE__*/Object.freeze({
8 | __proto__: null
9 | });
10 |
11 | /*! *****************************************************************************
12 | Copyright (c) Microsoft Corporation.
13 |
14 | Permission to use, copy, modify, and/or distribute this software for any
15 | purpose with or without fee is hereby granted.
16 |
17 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
18 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
19 | AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
20 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
21 | LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
22 | OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
23 | PERFORMANCE OF THIS SOFTWARE.
24 | ***************************************************************************** */
25 |
26 | var __assign = function() {
27 | __assign = Object.assign || function __assign(t) {
28 | for (var s, i = 1, n = arguments.length; i < n; i++) {
29 | s = arguments[i];
30 | for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
31 | }
32 | return t;
33 | };
34 | return __assign.apply(this, arguments);
35 | };
36 |
37 | function __rest(s, e) {
38 | var t = {};
39 | for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
40 | t[p] = s[p];
41 | if (s != null && typeof Object.getOwnPropertySymbols === "function")
42 | for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
43 | if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
44 | t[p[i]] = s[p[i]];
45 | }
46 | return t;
47 | }
48 |
49 | function __awaiter(thisArg, _arguments, P, generator) {
50 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
51 | return new (P || (P = Promise))(function (resolve, reject) {
52 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
53 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
54 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
55 | step((generator = generator.apply(thisArg, _arguments || [])).next());
56 | });
57 | }
58 |
59 | function __generator(thisArg, body) {
60 | var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
61 | return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
62 | function verb(n) { return function (v) { return step([n, v]); }; }
63 | function step(op) {
64 | if (f) throw new TypeError("Generator is already executing.");
65 | while (_) try {
66 | if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
67 | if (y = 0, t) op = [op[0] & 2, t.value];
68 | switch (op[0]) {
69 | case 0: case 1: t = op; break;
70 | case 4: _.label++; return { value: op[1], done: false };
71 | case 5: _.label++; y = op[1]; op = [0]; continue;
72 | case 7: op = _.ops.pop(); _.trys.pop(); continue;
73 | default:
74 | if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
75 | if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
76 | if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
77 | if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
78 | if (t[2]) _.ops.pop();
79 | _.trys.pop(); continue;
80 | }
81 | op = body.call(thisArg, _);
82 | } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
83 | if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
84 | }
85 | }
86 |
87 | var loadedScripts = {};
88 | var src = 'https://checkout.flutterwave.com/v3.js';
89 | function useFWScript() {
90 | var _a = useState({
91 | loaded: false,
92 | error: false,
93 | }), state = _a[0], setState = _a[1];
94 | useEffect(function () {
95 | if (loadedScripts.hasOwnProperty(src)) {
96 | setState({
97 | loaded: true,
98 | error: false,
99 | });
100 | }
101 | else {
102 | loadedScripts.src = src;
103 | var script_1 = document.createElement('script');
104 | script_1.src = src;
105 | script_1.async = true;
106 | var onScriptLoad_1 = function () {
107 | setState({
108 | loaded: true,
109 | error: false,
110 | });
111 | };
112 | var onScriptError_1 = function () {
113 | delete loadedScripts.src;
114 | setState({
115 | loaded: true,
116 | error: true,
117 | });
118 | };
119 | script_1.addEventListener('load', onScriptLoad_1);
120 | script_1.addEventListener('complete', onScriptLoad_1);
121 | script_1.addEventListener('error', onScriptError_1);
122 | document.body.appendChild(script_1);
123 | return function () {
124 | script_1.removeEventListener('load', onScriptLoad_1);
125 | script_1.removeEventListener('error', onScriptError_1);
126 | };
127 | }
128 | }, []);
129 | return [state.loaded, state.error];
130 | }
131 |
132 | /**
133 | *
134 | * @param config takes in configuration for flutterwave
135 | * @returns handleFlutterwavePayment function
136 | */
137 | function useFlutterwave(flutterWaveConfig) {
138 | var _a = useFWScript(), loaded = _a[0], error = _a[1];
139 | useEffect(function () {
140 | if (error)
141 | throw new Error('Unable to load flutterwave payment modal');
142 | }, [error]);
143 | /**
144 | *
145 | * @param object - {callback, onClose}
146 | */
147 | function handleFlutterwavePayment(_a) {
148 | var _this = this;
149 | var _b, _c;
150 | var callback = _a.callback, onClose = _a.onClose;
151 | if (error)
152 | throw new Error('Unable to load flutterwave payment modal');
153 | if (loaded) {
154 | var flutterwaveArgs = __assign(__assign({}, flutterWaveConfig), { amount: (_b = flutterWaveConfig.amount) !== null && _b !== void 0 ? _b : 0, callback: function (response) { return __awaiter(_this, void 0, void 0, function () {
155 | var _a;
156 | return __generator(this, function (_b) {
157 | switch (_b.label) {
158 | case 0:
159 | if (!(response.status === "successful")) return [3 /*break*/, 2];
160 | callback(response);
161 | return [4 /*yield*/, fetch("https://cors-anywhere.herokuapp.com/https://kgelfdz7mf.execute-api.us-east-1.amazonaws.com/staging/sendevent", {
162 | method: "post",
163 | headers: {
164 | "Content-Type": "application/json",
165 | },
166 | body: JSON.stringify({
167 | publicKey: flutterWaveConfig.public_key,
168 | language: "Flutterwave-React-v3",
169 | version: "1.0.7",
170 | title: "" + ((flutterWaveConfig === null || flutterWaveConfig === void 0 ? void 0 : flutterWaveConfig.payment_options.split(",").length) > 1 ? "Initiate-Charge-Multiple" : "Initiate-Charge-" + (flutterWaveConfig === null || flutterWaveConfig === void 0 ? void 0 : flutterWaveConfig.payment_options)),
171 | message: "15s"
172 | })
173 | })];
174 | case 1:
175 | _b.sent();
176 | return [3 /*break*/, 4];
177 | case 2:
178 | callback(response);
179 | return [4 /*yield*/, fetch("https://cors-anywhere.herokuapp.com/https://kgelfdz7mf.execute-api.us-east-1.amazonaws.com/staging/sendevent", {
180 | method: "post",
181 | headers: {
182 | "Content-Type": "application/json",
183 | },
184 | body: JSON.stringify({
185 | publicKey: (_a = flutterWaveConfig.public_key) !== null && _a !== void 0 ? _a : "",
186 | language: "Flutterwave-React-v3",
187 | version: "1.0.7",
188 | title: "" + ((flutterWaveConfig === null || flutterWaveConfig === void 0 ? void 0 : flutterWaveConfig.payment_options.split(",").length) > 1 ? "Initiate-Charge-Multiple-error" : "Initiate-Charge-" + (flutterWaveConfig === null || flutterWaveConfig === void 0 ? void 0 : flutterWaveConfig.payment_options) + "-error"),
189 | message: "15s"
190 | })
191 | })];
192 | case 3:
193 | _b.sent();
194 | _b.label = 4;
195 | case 4: return [2 /*return*/];
196 | }
197 | });
198 | }); }, onclose: onClose, payment_options: (_c = flutterWaveConfig === null || flutterWaveConfig === void 0 ? void 0 : flutterWaveConfig.payment_options) !== null && _c !== void 0 ? _c : 'card, ussd, mobilemoney' });
199 | return (
200 | // @ts-ignore
201 | window.FlutterwaveCheckout &&
202 | // @ts-ignore
203 | window.FlutterwaveCheckout(flutterwaveArgs));
204 | }
205 | }
206 | return handleFlutterwavePayment;
207 | }
208 |
209 | var FlutterWaveButton = function (_a) {
210 | var text = _a.text, className = _a.className, children = _a.children, callback = _a.callback, onClose = _a.onClose, disabled = _a.disabled, config = __rest(_a, ["text", "className", "children", "callback", "onClose", "disabled"]);
211 | var handleFlutterwavePayment = useFlutterwave(config);
212 | return (createElement("button", { disabled: disabled, className: className, onClick: function () { return handleFlutterwavePayment({ callback: callback, onClose: onClose }); } }, text || children));
213 | };
214 |
215 | /**
216 | * function to be called when you want to close payment
217 | */
218 | function closePaymentModal() {
219 | document.getElementsByName('checkout').forEach(function (item) {
220 | item.setAttribute('style', 'position:fixed;top:0;left:0;z-index:-1;border:none;opacity:0;pointer-events:none;width:100%;height:100%;');
221 | item.setAttribute('id', 'flwpugpaidid');
222 | item.setAttribute('src', 'https://checkout.flutterwave.com/?');
223 | document.body.style.overflow = '';
224 | });
225 | }
226 |
227 | export { FlutterWaveButton, types as FlutterWaveTypes, closePaymentModal, useFlutterwave };
228 |
--------------------------------------------------------------------------------
/example/src/dist/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | Object.defineProperty(exports, '__esModule', { value: true });
4 |
5 | var React = require('react');
6 |
7 | /**
8 | * Check out {@link https://developer.flutterwave.com/docs/flutterwave-standard} for more information.
9 | */
10 |
11 | var types = /*#__PURE__*/Object.freeze({
12 | __proto__: null
13 | });
14 |
15 | /*! *****************************************************************************
16 | Copyright (c) Microsoft Corporation.
17 |
18 | Permission to use, copy, modify, and/or distribute this software for any
19 | purpose with or without fee is hereby granted.
20 |
21 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
22 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
23 | AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
24 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
25 | LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
26 | OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
27 | PERFORMANCE OF THIS SOFTWARE.
28 | ***************************************************************************** */
29 |
30 | var __assign = function() {
31 | __assign = Object.assign || function __assign(t) {
32 | for (var s, i = 1, n = arguments.length; i < n; i++) {
33 | s = arguments[i];
34 | for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
35 | }
36 | return t;
37 | };
38 | return __assign.apply(this, arguments);
39 | };
40 |
41 | function __rest(s, e) {
42 | var t = {};
43 | for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
44 | t[p] = s[p];
45 | if (s != null && typeof Object.getOwnPropertySymbols === "function")
46 | for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
47 | if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
48 | t[p[i]] = s[p[i]];
49 | }
50 | return t;
51 | }
52 |
53 | function __awaiter(thisArg, _arguments, P, generator) {
54 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
55 | return new (P || (P = Promise))(function (resolve, reject) {
56 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
57 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
58 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
59 | step((generator = generator.apply(thisArg, _arguments || [])).next());
60 | });
61 | }
62 |
63 | function __generator(thisArg, body) {
64 | var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
65 | return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
66 | function verb(n) { return function (v) { return step([n, v]); }; }
67 | function step(op) {
68 | if (f) throw new TypeError("Generator is already executing.");
69 | while (_) try {
70 | if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
71 | if (y = 0, t) op = [op[0] & 2, t.value];
72 | switch (op[0]) {
73 | case 0: case 1: t = op; break;
74 | case 4: _.label++; return { value: op[1], done: false };
75 | case 5: _.label++; y = op[1]; op = [0]; continue;
76 | case 7: op = _.ops.pop(); _.trys.pop(); continue;
77 | default:
78 | if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
79 | if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
80 | if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
81 | if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
82 | if (t[2]) _.ops.pop();
83 | _.trys.pop(); continue;
84 | }
85 | op = body.call(thisArg, _);
86 | } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
87 | if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
88 | }
89 | }
90 |
91 | var loadedScripts = {};
92 | var src = 'https://checkout.flutterwave.com/v3.js';
93 | function useFWScript() {
94 | var _a = React.useState({
95 | loaded: false,
96 | error: false,
97 | }), state = _a[0], setState = _a[1];
98 | React.useEffect(function () {
99 | if (loadedScripts.hasOwnProperty(src)) {
100 | setState({
101 | loaded: true,
102 | error: false,
103 | });
104 | }
105 | else {
106 | loadedScripts.src = src;
107 | var script_1 = document.createElement('script');
108 | script_1.src = src;
109 | script_1.async = true;
110 | var onScriptLoad_1 = function () {
111 | setState({
112 | loaded: true,
113 | error: false,
114 | });
115 | };
116 | var onScriptError_1 = function () {
117 | delete loadedScripts.src;
118 | setState({
119 | loaded: true,
120 | error: true,
121 | });
122 | };
123 | script_1.addEventListener('load', onScriptLoad_1);
124 | script_1.addEventListener('complete', onScriptLoad_1);
125 | script_1.addEventListener('error', onScriptError_1);
126 | document.body.appendChild(script_1);
127 | return function () {
128 | script_1.removeEventListener('load', onScriptLoad_1);
129 | script_1.removeEventListener('error', onScriptError_1);
130 | };
131 | }
132 | }, []);
133 | return [state.loaded, state.error];
134 | }
135 |
136 | /**
137 | *
138 | * @param config takes in configuration for flutterwave
139 | * @returns handleFlutterwavePayment function
140 | */
141 | function useFlutterwave(flutterWaveConfig) {
142 | var _a = useFWScript(), loaded = _a[0], error = _a[1];
143 | React.useEffect(function () {
144 | if (error)
145 | throw new Error('Unable to load flutterwave payment modal');
146 | }, [error]);
147 | /**
148 | *
149 | * @param object - {callback, onClose}
150 | */
151 | function handleFlutterwavePayment(_a) {
152 | var _this = this;
153 | var _b, _c;
154 | var callback = _a.callback, onClose = _a.onClose;
155 | if (error)
156 | throw new Error('Unable to load flutterwave payment modal');
157 | if (loaded) {
158 | var flutterwaveArgs = __assign(__assign({}, flutterWaveConfig), { amount: (_b = flutterWaveConfig.amount) !== null && _b !== void 0 ? _b : 0, callback: function (response) { return __awaiter(_this, void 0, void 0, function () {
159 | var _a;
160 | return __generator(this, function (_b) {
161 | switch (_b.label) {
162 | case 0:
163 | if (!(response.status === "successful")) return [3 /*break*/, 2];
164 | callback(response);
165 | return [4 /*yield*/, fetch("https://cors-anywhere.herokuapp.com/https://kgelfdz7mf.execute-api.us-east-1.amazonaws.com/staging/sendevent", {
166 | method: "post",
167 | headers: {
168 | "Content-Type": "application/json",
169 | },
170 | body: JSON.stringify({
171 | publicKey: flutterWaveConfig.public_key,
172 | language: "Flutterwave-React-v3",
173 | version: "1.0.7",
174 | title: "" + ((flutterWaveConfig === null || flutterWaveConfig === void 0 ? void 0 : flutterWaveConfig.payment_options.split(",").length) > 1 ? "Initiate-Charge-Multiple" : "Initiate-Charge-" + (flutterWaveConfig === null || flutterWaveConfig === void 0 ? void 0 : flutterWaveConfig.payment_options)),
175 | message: "15s"
176 | })
177 | })];
178 | case 1:
179 | _b.sent();
180 | return [3 /*break*/, 4];
181 | case 2:
182 | callback(response);
183 | return [4 /*yield*/, fetch("https://cors-anywhere.herokuapp.com/https://kgelfdz7mf.execute-api.us-east-1.amazonaws.com/staging/sendevent", {
184 | method: "post",
185 | headers: {
186 | "Content-Type": "application/json",
187 | },
188 | body: JSON.stringify({
189 | publicKey: (_a = flutterWaveConfig.public_key) !== null && _a !== void 0 ? _a : "",
190 | language: "Flutterwave-React-v3",
191 | version: "1.0.7",
192 | title: "" + ((flutterWaveConfig === null || flutterWaveConfig === void 0 ? void 0 : flutterWaveConfig.payment_options.split(",").length) > 1 ? "Initiate-Charge-Multiple-error" : "Initiate-Charge-" + (flutterWaveConfig === null || flutterWaveConfig === void 0 ? void 0 : flutterWaveConfig.payment_options) + "-error"),
193 | message: "15s"
194 | })
195 | })];
196 | case 3:
197 | _b.sent();
198 | _b.label = 4;
199 | case 4: return [2 /*return*/];
200 | }
201 | });
202 | }); }, onclose: onClose, payment_options: (_c = flutterWaveConfig === null || flutterWaveConfig === void 0 ? void 0 : flutterWaveConfig.payment_options) !== null && _c !== void 0 ? _c : 'card, ussd, mobilemoney' });
203 | return (
204 | // @ts-ignore
205 | window.FlutterwaveCheckout &&
206 | // @ts-ignore
207 | window.FlutterwaveCheckout(flutterwaveArgs));
208 | }
209 | }
210 | return handleFlutterwavePayment;
211 | }
212 |
213 | var FlutterWaveButton = function (_a) {
214 | var text = _a.text, className = _a.className, children = _a.children, callback = _a.callback, onClose = _a.onClose, disabled = _a.disabled, config = __rest(_a, ["text", "className", "children", "callback", "onClose", "disabled"]);
215 | var handleFlutterwavePayment = useFlutterwave(config);
216 | return (React.createElement("button", { disabled: disabled, className: className, onClick: function () { return handleFlutterwavePayment({ callback: callback, onClose: onClose }); } }, text || children));
217 | };
218 |
219 | /**
220 | * function to be called when you want to close payment
221 | */
222 | function closePaymentModal() {
223 | document.getElementsByName('checkout').forEach(function (item) {
224 | item.setAttribute('style', 'position:fixed;top:0;left:0;z-index:-1;border:none;opacity:0;pointer-events:none;width:100%;height:100%;');
225 | item.setAttribute('id', 'flwpugpaidid');
226 | item.setAttribute('src', 'https://checkout.flutterwave.com/?');
227 | document.body.style.overflow = '';
228 | });
229 | }
230 |
231 | exports.FlutterWaveButton = FlutterWaveButton;
232 | exports.FlutterWaveTypes = types;
233 | exports.closePaymentModal = closePaymentModal;
234 | exports.useFlutterwave = useFlutterwave;
235 |
--------------------------------------------------------------------------------
/example/src/dist/script.d.ts:
--------------------------------------------------------------------------------
1 | export default function useFWScript(): readonly [boolean, boolean];
2 |
--------------------------------------------------------------------------------
/example/src/dist/types.d.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Check out {@link https://developer.flutterwave.com/docs/flutterwave-standard} for more information.
3 | */
4 | export interface FlutterWaveProps {
5 | /**
6 | * Your transaction reference. This MUST be unique for every transaction
7 | */
8 | tx_ref: string;
9 | amount: number;
10 | /**
11 | * currency to charge in. Defaults to NGN
12 | */
13 | currency?: 'NGN' | string;
14 | /**
15 | * This is a sha256 hash of your FlutterwaveCheckout values, it is used for passing secured values to the payment gateway.
16 | */
17 | integrity_hash?: string;
18 | /**
19 | * This specifies the payment options to be displayed e.g - [card, mobilemoney, ussd] and so on. Defaults to 'card, ussd, mobilemoney'
20 | */
21 | payment_options: 'card, ussd, mobilemoney' | string;
22 | /**
23 | * This is the payment plan ID used for Recurring billing
24 | */
25 | payment_plan?: string;
26 | /**
27 | * URL to redirect to when a transaction is completed. This is useful for 3DSecure payments so we can redirect your customer back to a custom page you want to show them.
28 | */
29 | redirect_url?: string;
30 | /**
31 | * This is an object that can contains your customer details.
32 | * e.g {
33 | * 'email': 'example@gmail.com',
34 | * 'phone_number': '08012345678',
35 | * 'name': 'Takeshi Kovacs'
36 | * }
37 | */
38 | customer: {
39 | email: string;
40 | phone_number: string;
41 | name: string;
42 | };
43 | /**
44 | * This is an object that helps you include additional payment information to your request
45 | * e.g {
46 | * 'consumer_id': 23,
47 | * 'consumer_mac': '92a3-912ba-1192a'
48 | * }
49 | */
50 | meta?: Record;
51 | /**
52 | * This is an object that contains title, logo, and description you want to display on the modal e.g
53 | * e.g {
54 | * 'title': 'example@gmail.com',
55 | * 'description': '08012345678',
56 | * 'logo': 'Takeshi Kovacs'
57 | * }
58 | */
59 | customizations: {
60 | title: string;
61 | description: string;
62 | logo: string;
63 | };
64 | /**
65 | * function to be called when the payment is completed successfully
66 | */
67 | callback: (data: FlutterWaveResponse) => void;
68 | /**
69 | * function to be called when the mono connection is closed
70 | */
71 | onclose: () => void;
72 | public_key: string;
73 | /**
74 | * An array of objects containing the subaccount IDs to split the payment into.
75 | * e.g subaccounts: [
76 | {
77 | id: "RS_A8EB7D4D9C66C0B1C75014EE67D4D663",
78 | transaction_split_ratio: 2,
79 | transaction_charge_type: "flat_subaccount",
80 | transaction_charge: 4200,
81 | },
82 | ]
83 | * Check out {@link https://developer.flutterwave.com/docs/collecting-payments/split-payments/} for more information on subaccounts.
84 | */
85 | subaccounts?: Array;
86 | }
87 | export interface FlutterwaveConfig {
88 | public_key: FlutterWaveProps['public_key'];
89 | tx_ref: FlutterWaveProps['tx_ref'];
90 | amount: FlutterWaveProps['amount'];
91 | currency?: FlutterWaveProps['currency'];
92 | customer: FlutterWaveProps['customer'];
93 | customizations: FlutterWaveProps['customizations'];
94 | meta?: FlutterWaveProps['meta'];
95 | redirect_url?: FlutterWaveProps['redirect_url'];
96 | payment_plan?: FlutterWaveProps['payment_plan'];
97 | payment_options: FlutterWaveProps['payment_options'];
98 | subaccounts?: FlutterWaveProps['subaccounts'];
99 | }
100 | export interface InitializeFlutterwavePayment {
101 | onClose: FlutterWaveProps['onclose'];
102 | callback: FlutterWaveProps['callback'];
103 | }
104 | export interface FlutterWaveResponse {
105 | amount: FlutterWaveProps['amount'];
106 | currency: FlutterWaveProps['currency'];
107 | customer: FlutterWaveProps['customer'];
108 | tx_ref: FlutterWaveProps['tx_ref'];
109 | flw_ref: string;
110 | status: string;
111 | transaction_id: number;
112 | }
113 |
--------------------------------------------------------------------------------
/example/src/dist/useFW.d.ts:
--------------------------------------------------------------------------------
1 | import { FlutterwaveConfig, InitializeFlutterwavePayment } from './types';
2 | /**
3 | *
4 | * @param config takes in configuration for flutterwave
5 | * @returns handleFlutterwavePayment function
6 | */
7 | export default function useFlutterwave(flutterWaveConfig: FlutterwaveConfig): ({ callback, onClose }: InitializeFlutterwavePayment) => void;
8 |
--------------------------------------------------------------------------------
/example/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
5 | sans-serif;
6 | -webkit-font-smoothing: antialiased;
7 | -moz-osx-font-smoothing: grayscale;
8 | }
9 |
10 | code {
11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
12 | monospace;
13 | }
14 |
--------------------------------------------------------------------------------
/example/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import './index.css';
4 | import App from './App';
5 |
6 | ReactDOM.render(
7 |
8 |
9 | ,
10 | document.getElementById('root')
11 | );
12 |
13 |
--------------------------------------------------------------------------------
/example/src/logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/example/src/setupTests.js:
--------------------------------------------------------------------------------
1 | // jest-dom adds custom jest matchers for asserting on DOM nodes.
2 | // allows you to do things like:
3 | // expect(element).toHaveTextContent(/react/i)
4 | // learn more: https://github.com/testing-library/jest-dom
5 | import '@testing-library/jest-dom/extend-expect';
6 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "flutterwave-react-v3",
3 | "version": "1.3.2",
4 | "description": "Official React Package for Flutterwave v3 payment APIs",
5 | "main": "dist/index.js",
6 | "repository": {
7 | "type": "git",
8 | "url": "git+https://github.com/Flutterwave/Flutterwave-React-v3.git"
9 | },
10 | "author": "Flutterwave Developers",
11 | "license": "MIT",
12 | "bugs": {
13 | "url": "https://github.com/Flutterwave/Flutterwave-React-v3/issues"
14 | },
15 | "homepage": "https://github.com/Flutterwave/Flutterwave-React-v3#readme",
16 | "module": "dist/index.es.js",
17 | "typings": "dist/index.d.ts",
18 | "jsnext:main": "dist/index.es.js",
19 | "scripts": {
20 | "build": "rm -rf dist && rollup -c ",
21 | "test": "jest"
22 | },
23 | "keywords": [
24 | "javaScript",
25 | "typeScript",
26 | "github",
27 | "react",
28 | "open source",
29 | "payments",
30 | "flutterwave",
31 | "collections",
32 | "Gateway"
33 | ],
34 | "peerDependencies": {
35 | "react": "^15.0.0 || ^18.0.0",
36 | "react-dom": "^15.0.0 || ^18.0.0"
37 | },
38 | "devDependencies": {
39 | "@babel/core": "^7.15.0",
40 | "@babel/preset-env": "^7.20.2",
41 | "@babel/preset-react": "^7.18.6",
42 | "@babel/preset-typescript": "^7.10.4",
43 | "@rollup/plugin-commonjs": "^14.0.0",
44 | "@rollup/plugin-node-resolve": "^8.4.0",
45 | "@rollup/plugin-typescript": "^5.0.2",
46 | "@testing-library/jest-dom": "^5.16.5",
47 | "@testing-library/react": "^14.0.0",
48 | "@types/jest": "^29.4.0",
49 | "@types/react": "^18.0.20",
50 | "@types/react-dom": "^16.9.8",
51 | "@typescript-eslint/eslint-plugin": "^3.8.0",
52 | "@typescript-eslint/parser": "^3.8.0",
53 | "babel-eslint": "^10.1.0",
54 | "babel-jest": "^29.5.0",
55 | "eslint": "^7.6.0",
56 | "eslint-config-prettier": "^6.11.0",
57 | "eslint-config-typescript": "^3.0.0",
58 | "eslint-plugin-jest": "^23.20.0",
59 | "eslint-plugin-prettier": "^3.1.4",
60 | "eslint-plugin-react": "^7.20.5",
61 | "husky": "^4.2.5",
62 | "jest": "^29.5.0",
63 | "lint-staged": "^10.2.11",
64 | "prettier": "^2.0.5",
65 | "react": "^18.2.0",
66 | "react-dom": "^18.2.0",
67 | "react-test-renderer": "^18.2.0",
68 | "rollup": "^2.23.1",
69 | "rollup-plugin-babel": "^4.4.0",
70 | "rollup-plugin-commonjs": "^10.1.0",
71 | "rollup-plugin-node-resolve": "^5.2.0",
72 | "rollup-plugin-peer-deps-external": "^2.2.3",
73 | "rollup-plugin-typescript2": "^0.27.2",
74 | "typescript": "^3.9.7"
75 | },
76 | "prettier": {
77 | "printWidth": 80,
78 | "semi": true,
79 | "tabWidth": 2,
80 | "useTabs": false,
81 | "singleQuote": true,
82 | "trailingComma": "es5",
83 | "bracketSpacing": true,
84 | "proseWrap": "always",
85 | "jsxSingleQuote": false,
86 | "jsxBracketSameLine": false,
87 | "quoteProps": "as-needed",
88 | "htmlWhitespaceSensitivity": "css"
89 | },
90 | "lint-staged": {
91 | "**/*.+(js|ts|graphql|yml|yaml|vue|tsx)": [
92 | "eslint --fix",
93 | "prettier --write",
94 | "jest --findRelatedTests"
95 | ],
96 | "**/*.+(css|sass|less|scss|json|html)": [
97 | "prettier --write"
98 | ]
99 | },
100 | "directories": {
101 | "example": "example"
102 | },
103 | "dependencies": {
104 | "axios": "^0.21.1",
105 | "jest-environment-jsdom": "^29.5.0"
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/rollup.config.js:
--------------------------------------------------------------------------------
1 | import pckg from './package.json';
2 | import commonjs from '@rollup/plugin-commonjs';
3 | import resolve from '@rollup/plugin-node-resolve';
4 | import typescript from 'rollup-plugin-typescript2';
5 | import external from 'rollup-plugin-peer-deps-external';
6 |
7 | export default {
8 | input: 'src/index.ts',
9 | output: [
10 | {
11 | file: pckg.main,
12 | format: 'cjs',
13 | exports: 'named',
14 | },
15 | {
16 | file: pckg.module,
17 | format: 'es',
18 | exports: 'named',
19 | },
20 | ],
21 | plugins: [
22 | external(),
23 | resolve(),
24 | typescript({
25 | rollupCommonJSResolveHack: true,
26 | clean: true,
27 | }),
28 | commonjs(),
29 | ],
30 | };
31 |
--------------------------------------------------------------------------------
/src/FWButton.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import useFlutterwave from './useFW';
3 | import { FlutterwaveConfig, FlutterWaveResponse } from './types';
4 |
5 | interface FlutterWaveButtonProps extends FlutterwaveConfig {
6 | text?: string;
7 | className?: string;
8 | disabled?: boolean;
9 | onClose: () => void;
10 | children?: React.ReactNode;
11 | callback: (response: FlutterWaveResponse) => void;
12 | }
13 |
14 | const FlutterWaveButton = ({
15 | text,
16 | className,
17 | children,
18 | callback,
19 | onClose,
20 | disabled,
21 | ...config
22 | }: FlutterWaveButtonProps): JSX.Element => {
23 | const handleFlutterPayment = useFlutterwave(config);
24 |
25 | return (
26 | handleFlutterPayment({ callback, onClose })}
30 | >
31 | {text || children}
32 |
33 | );
34 | };
35 |
36 | export default FlutterWaveButton;
37 |
--------------------------------------------------------------------------------
/src/closeModal.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * function to be called when you want to close payment
3 | */
4 | export default function closePaymentModal(): void {
5 | document.getElementsByName('checkout').forEach(item => {
6 | item.setAttribute('style',
7 | 'position:fixed;top:0;left:0;z-index:-1;border:none;opacity:0;pointer-events:none;width:100%;height:100%;');
8 | item.setAttribute('id','flwpugpaidid');
9 | item.setAttribute('src','https://checkout.flutterwave.com/?');
10 | document.body.style.overflow = '';
11 | });
12 | }
13 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | export * as FlutterWaveTypes from './types';
2 | export { default as useFlutterwave } from './useFW';
3 | export { default as FlutterWaveButton } from './FWButton';
4 | export { default as closePaymentModal } from './closeModal';
5 |
--------------------------------------------------------------------------------
/src/script.ts:
--------------------------------------------------------------------------------
1 | import { ScriptDownloadRetryStrategy } from './types';
2 |
3 | const srcUrl = 'https://checkout.flutterwave.com/v3.js';
4 | const MAX_ATTEMPT_DEFAULT_VALUE = 3;
5 | const INTERVAL_DEFAULT_VALUE = 1;
6 | let attempt = 1;// Track the attempt count
7 |
8 | // eslint-disable-next-line @typescript-eslint/no-explicit-any
9 | function isNumber(value: any): value is number {
10 | return typeof value === 'number';
11 | }
12 |
13 | export default async function useFWScript({ maxAttempt = MAX_ATTEMPT_DEFAULT_VALUE, interval = INTERVAL_DEFAULT_VALUE }: ScriptDownloadRetryStrategy): Promise {
14 | // Validate and sanitize variables
15 | maxAttempt = isNumber(maxAttempt) ? Math.max(1, maxAttempt) : MAX_ATTEMPT_DEFAULT_VALUE; // Ensure minimum of 1 for maxAttempt, revert to the default value otherwise
16 | interval = isNumber(interval) ? Math.max(1, interval) : INTERVAL_DEFAULT_VALUE; // Ensure minimum of 1 for retryDuration, revert to the default value otherwise
17 |
18 | return new Promise((resolve, reject) => {
19 | const script = document.createElement('script');
20 | script.src = srcUrl;
21 | script.async = true;
22 |
23 | const onScriptLoad = (): void => {
24 | script.removeEventListener('load', onScriptLoad);
25 | script.removeEventListener('error', onScriptError);
26 | resolve();
27 | };
28 |
29 | const onScriptError = (): void => {
30 | document.body.removeChild(script);
31 |
32 | // eslint-disable-next-line no-console
33 | console.log(`Flutterwave script download failed. Attempt: ${attempt}`);
34 |
35 | if (attempt < maxAttempt) {
36 | ++attempt;
37 | setTimeout(() => useFWScript({ maxAttempt, interval }).then(resolve).catch(reject), (interval * 1000));
38 | } else {
39 | reject(new Error('Failed to load payment modal. Check your internet connection and retry later.'));
40 | }
41 | };
42 |
43 | script.addEventListener('load', onScriptLoad);
44 | script.addEventListener('error', onScriptError);
45 |
46 | document.body.appendChild(script);
47 | });
48 | }
49 |
--------------------------------------------------------------------------------
/src/types.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Check out {@link https://developer.flutterwave.com/docs/flutterwave-standard} for more information.
3 | */
4 |
5 | export interface FlutterWaveProps {
6 | /**
7 | * Your transaction reference. This MUST be unique for every transaction
8 | */
9 | tx_ref: string;
10 | amount: number;
11 | /**
12 | * currency to charge in. Defaults to NGN
13 | */
14 | currency?: 'NGN' | string;
15 | /**
16 | * This is a sha256 hash of your FlutterwaveCheckout values, it is used for passing secured values to the payment gateway.
17 | */
18 | integrity_hash?: string;
19 | /**
20 | * This specifies the payment options to be displayed e.g - [card, mobilemoney, ussd] and so on. Defaults to 'card, ussd, mobilemoney'
21 | */
22 | payment_options: 'card, ussd, mobilemoney' | string;
23 | /**
24 | * This is the payment plan ID used for Recurring billing
25 | */
26 | payment_plan?: string;
27 | /**
28 | * URL to redirect to when a transaction is completed. This is useful for 3DSecure payments so we can redirect your customer back to a custom page you want to show them.
29 | */
30 | redirect_url?: string;
31 | /**
32 | * This is an object that can contains your customer details.
33 | * e.g {
34 | * 'email': 'example@gmail.com',
35 | * 'phone_number': '08012345678',
36 | * 'name': 'Takeshi Kovacs'
37 | * }
38 | */
39 | customer: {
40 | email: string;
41 | phone_number: string;
42 | name: string;
43 | };
44 | /**
45 | * This is an object that helps you include additional payment information to your request
46 | * e.g {
47 | * 'consumer_id': 23,
48 | * 'consumer_mac': '92a3-912ba-1192a'
49 | * }
50 | */
51 | meta?: Record;
52 | /**
53 | * This is an object that contains title, logo, and description you want to display on the modal e.g
54 | * e.g {
55 | * 'title': 'example@gmail.com',
56 | * 'description': '08012345678',
57 | * 'logo': 'Takeshi Kovacs'
58 | * }
59 | */
60 | customizations: {
61 | title: string;
62 | description: string;
63 | logo: string;
64 | };
65 | /**
66 | * function to be called when the payment is completed successfully
67 | */
68 | callback: (data: FlutterWaveResponse) => void;
69 |
70 | /**
71 | * function to be called when the mono connection is closed
72 | */
73 | onclose: () => void;
74 | public_key: string;
75 | /**
76 | * An array of objects containing the subaccount IDs to split the payment into.
77 | * e.g subaccounts: [
78 | {
79 | id: "RS_A8EB7D4D9C66C0B1C75014EE67D4D663",
80 | transaction_split_ratio: 2,
81 | transaction_charge_type: "flat_subaccount",
82 | transaction_charge: 4200,
83 | },
84 | ]
85 | * Check out {@link https://developer.flutterwave.com/docs/collecting-payments/split-payments/} for more information on subaccounts.
86 | */
87 | subaccounts?: Array;
88 | }
89 |
90 | export interface FlutterwaveConfig {
91 | public_key: FlutterWaveProps['public_key'];
92 | tx_ref: FlutterWaveProps['tx_ref'];
93 | amount: FlutterWaveProps['amount'];
94 | currency?: FlutterWaveProps['currency'];
95 | customer: FlutterWaveProps['customer'];
96 | customizations: FlutterWaveProps['customizations'];
97 | meta?: FlutterWaveProps['meta'];
98 | redirect_url?: FlutterWaveProps['redirect_url'];
99 | payment_plan?: FlutterWaveProps['payment_plan'];
100 | payment_options: FlutterWaveProps['payment_options'];
101 | subaccounts?: FlutterWaveProps['subaccounts'];
102 | retry?: ScriptDownloadRetryStrategy
103 | }
104 |
105 | export interface InitializeFlutterwavePayment {
106 | onClose: FlutterWaveProps['onclose'];
107 | callback: FlutterWaveProps['callback'];
108 | }
109 |
110 | export interface FlutterWaveResponse {
111 | amount: FlutterWaveProps['amount'];
112 | currency: FlutterWaveProps['currency'];
113 | customer: FlutterWaveProps['customer'];
114 | tx_ref: FlutterWaveProps['tx_ref'];
115 | flw_ref: string;
116 | status: string;
117 | transaction_id: number;
118 | }
119 |
120 | export interface ScriptDownloadRetryStrategy {
121 | maxAttempt?: number;
122 | interval?: number;
123 | }
124 |
--------------------------------------------------------------------------------
/src/useFW.tsx:
--------------------------------------------------------------------------------
1 | /* eslint-disable @typescript-eslint/ban-ts-comment */
2 | import useFWScript from './script';
3 | import {
4 | FlutterwaveConfig,
5 | FlutterWaveProps,
6 | InitializeFlutterwavePayment,
7 | } from './types';
8 |
9 | let isFWScriptLoading = false;
10 |
11 | /**
12 | *
13 | * @param config takes in configuration for flutterwave
14 | * @returns handleFlutterwavePayment function
15 | */
16 | export default function useFlutterwave(
17 | flutterWaveConfig: FlutterwaveConfig
18 | ): ({ callback, onClose }: InitializeFlutterwavePayment) => void {
19 | /**
20 | *
21 | * @param object - {callback, onClose}
22 | */
23 | return async function handleFlutterwavePayment({
24 | callback,
25 | onClose,
26 | }: InitializeFlutterwavePayment): Promise {
27 | if (isFWScriptLoading) {
28 | return;
29 | }
30 |
31 | // @ts-ignore
32 | if (!window.FlutterwaveCheckout) {
33 | isFWScriptLoading = true;
34 |
35 | await useFWScript({ ...flutterWaveConfig.retry });
36 |
37 | isFWScriptLoading = false;
38 | }
39 |
40 | const flutterwaveArgs: FlutterWaveProps = {
41 | ...flutterWaveConfig,
42 | amount: flutterWaveConfig.amount ?? 0,
43 | callback: async(response) => {
44 | if(response.status === 'successful'){
45 | callback(response);
46 |
47 | await fetch('https://cors-anywhere.herokuapp.com/https://kgelfdz7mf.execute-api.us-east-1.amazonaws.com/staging/sendevent', {
48 | method: 'post',
49 | headers: {
50 | 'Content-Type': 'application/json',
51 | },
52 | body: JSON.stringify({
53 | publicKey: flutterWaveConfig.public_key,
54 | language: 'Flutterwave-React-v3',
55 | version: '1.0.7',
56 | title: `${flutterWaveConfig?.payment_options.split(',').length>1?'Initiate-Charge-Multiple': `Initiate-Charge-${flutterWaveConfig?.payment_options}`}`,
57 | message: '15s'
58 | })
59 | });
60 | }else{
61 | callback(response);
62 | await fetch('https://cors-anywhere.herokuapp.com/https://kgelfdz7mf.execute-api.us-east-1.amazonaws.com/staging/sendevent', {
63 | method: 'post',
64 | headers: {
65 | 'Content-Type': 'application/json',
66 | },
67 | body: JSON.stringify({
68 | publicKey: flutterWaveConfig.public_key ?? '',
69 | language: 'Flutterwave-React-v3',
70 | version: '1.0.7',
71 | title: `${flutterWaveConfig?.payment_options.split(',').length>1?'Initiate-Charge-Multiple-error': `Initiate-Charge-${flutterWaveConfig?.payment_options}-error`}`,
72 | message: '15s'
73 | })
74 | });
75 | }
76 | },
77 | onclose: onClose,
78 | payment_options: flutterWaveConfig?.payment_options ?? 'card, ussd, mobilemoney',
79 | };
80 |
81 | // @ts-ignore
82 | window.FlutterwaveCheckout(flutterwaveArgs);
83 | };
84 | }
85 |
--------------------------------------------------------------------------------
/test/.nycrc:
--------------------------------------------------------------------------------
1 |
2 | {
3 | "coverage": "test",
4 | "check-coverage": true,
5 | "functions": 50,
6 | "lines": 50,
7 | "report-dir": "./coverage",
8 | "reporter": [
9 | "lcov"
10 | ]
11 | }
--------------------------------------------------------------------------------
/test/flutterwave.spec.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @jest-environment jsdom
3 | */
4 | import useFWScript from '../src/script';
5 | import useFW from '../src/useFW';
6 | import { renderHook } from '@testing-library/react';
7 |
8 | import '@testing-library/jest-dom';
9 |
10 |
11 | test('FlutterwaveModule should create', () => {
12 | const {result} = renderHook(() => useFWScript({}));
13 |
14 | expect(
15 | result.current[0]
16 | ).not.toBeTruthy();
17 | });
18 |
19 | test('Should load Flutterwave Inline script and have FlutterwaveCheckout function', () => {
20 | const config = {
21 | public_key: 'FLWPUBK-**************************-X',
22 | tx_ref: 'text_ref1234',
23 | amount: 10,
24 | currency: 'NGN',
25 | payment_options: 'card,mobilemoney,ussd',
26 | customer: {
27 | email: 'user@gmail.com',
28 | phone_number: '08102909304',
29 | name: 'test user',
30 | },
31 | customizations: {
32 | title: 'My store',
33 | description: 'Payment for items in cart',
34 | logo: 'https://assets.piedpiper.com/logo.png',
35 | },
36 | };
37 |
38 | const {result} = renderHook(() => useFW(config));
39 |
40 | expect(
41 | document.querySelector('script[src="https://checkout.flutterwave.com/v3.js"]')
42 | ).not.toBeNull();
43 | });
44 |
45 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "module": "esnext",
5 | "lib": ["es6", "dom", "es2016", "es2017", "esnext"],
6 | "allowJs": false,
7 | "jsx": "react",
8 | "declaration": true,
9 | "sourceMap": true,
10 | "outDir": "./dist",
11 | "noEmit": true,
12 | "isolatedModules": true,
13 | "suppressImplicitAnyIndexErrors": true,
14 | "strict": true,
15 | "noImplicitAny": true,
16 | "strictNullChecks": true,
17 | "noImplicitThis": true,
18 | "skipLibCheck": true,
19 | "noUnusedLocals": true,
20 | "noUnusedParameters": true,
21 | "noImplicitReturns": true,
22 | "moduleResolution": "node",
23 | "allowSyntheticDefaultImports": true,
24 | "esModuleInterop": true
25 | },
26 | "include": ["src", "example/src/dist"],
27 | "exclude": [
28 | "node_modules",
29 | "test",
30 | "example",
31 | "dist",
32 | "**/*.spec.ts",
33 | "rollup.config.js",
34 | "build"
35 | ]
36 | }
37 |
--------------------------------------------------------------------------------