@bigbinary/neeto-payments-frontend 4.0.5 → 4.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +798 -541
- package/app/javascript/src/translations/en.json +5 -5
- package/dist/AccountsDashboard.js +7 -7
- package/dist/AccountsDashboard.js.map +1 -1
- package/dist/ManualUpiPayment.js +2 -2
- package/dist/ManualUpiPayment.js.map +1 -1
- package/dist/PayoutsDashboard.js +7 -7
- package/dist/PayoutsDashboard.js.map +1 -1
- package/dist/cjs/AccountsDashboard.js +7 -7
- package/dist/cjs/AccountsDashboard.js.map +1 -1
- package/dist/cjs/ManualUpiPayment.js +2 -2
- package/dist/cjs/ManualUpiPayment.js.map +1 -1
- package/dist/cjs/PayoutsDashboard.js +7 -7
- package/dist/cjs/PayoutsDashboard.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,64 +1,72 @@
|
|
|
1
1
|
# neeto-payments-nano
|
|
2
2
|
|
|
3
|
-
The `neeto-payments-nano` is a comprehensive payment processing solution
|
|
3
|
+
The `neeto-payments-nano` is a comprehensive payment processing solution
|
|
4
|
+
designed for the Neeto ecosystem. Implemented as a Ruby on Rails engine with
|
|
5
|
+
associated React frontend components (`@bigbinary/neeto-payments-frontend`), it
|
|
6
|
+
provides a unified interface for managing payments across different providers,
|
|
7
|
+
abstracting away provider-specific complexities.
|
|
4
8
|
|
|
5
9
|
This engine enables host applications to:
|
|
6
10
|
|
|
7
|
-
-
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
11
|
+
- Integrate with multiple payment providers (Stripe, Razorpay, UPI).
|
|
12
|
+
- Process payments.
|
|
13
|
+
- Handle payment splits for marketplace scenarios (Stripe).
|
|
14
|
+
- Manage refunds.
|
|
15
|
+
- Store and reuse payment methods securely (optional).
|
|
16
|
+
- Configure fee structures and apply taxes/discounts.
|
|
17
|
+
- Provide administrative dashboards for monitoring transactions, refunds,
|
|
18
|
+
payouts, and accounts.
|
|
14
19
|
|
|
15
20
|
# Table of Contents
|
|
16
21
|
|
|
17
|
-
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
-
|
|
37
|
-
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
-
|
|
43
|
-
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
-
|
|
47
|
-
-
|
|
48
|
-
-
|
|
49
|
-
-
|
|
50
|
-
-
|
|
51
|
-
-
|
|
52
|
-
-
|
|
53
|
-
-
|
|
54
|
-
-
|
|
22
|
+
- [Installation (Backend Engine)](#installation-backend-engine)
|
|
23
|
+
- [1. Add the Gem](#1-add-the-gem)
|
|
24
|
+
- [2. Install Gems](#2-install-gems)
|
|
25
|
+
- [3. Install Migrations](#3-install-migrations)
|
|
26
|
+
- [4. Run Migrations](#4-run-migrations)
|
|
27
|
+
- [5. Mount the Engine](#5-mount-the-engine)
|
|
28
|
+
- [Configuration (Backend Engine)](#configuration-backend-engine)
|
|
29
|
+
- [1. Initializer](#1-initializer)
|
|
30
|
+
- [2. Model Associations](#2-model-associations)
|
|
31
|
+
- [3. Secrets and Credentials](#3-secrets-and-credentials)
|
|
32
|
+
- [4. Stripe Connect Signup](#4-stripe-connect-signup)
|
|
33
|
+
- [5. OAuth Callback URI Registration](#5-oauth-callback-uri-registration)
|
|
34
|
+
- [Frontend Integration](#frontend-integration)
|
|
35
|
+
- [1. Install Frontend Package](#1-install-frontend-package)
|
|
36
|
+
- [2. Install Peer Dependencies](#2-install-peer-dependencies)
|
|
37
|
+
- [3. Components](#3-components)
|
|
38
|
+
- [4. Hooks](#4-hooks)
|
|
39
|
+
- [5. Constants](#5-constants)
|
|
40
|
+
- [6. API Calls - Specify Providers](#4-api-calls---specify-providers)
|
|
41
|
+
- [Core Concepts](#core-concepts)
|
|
42
|
+
- [Webhook Handling](#webhook-handling)
|
|
43
|
+
- [Development Environment Setup](#development-environment-setup)
|
|
44
|
+
- [1. Tunneling](#1-tunneling)
|
|
45
|
+
- [2. Stripe Webhook Setup (Manual)](#2-stripe-webhook-setup-manual)
|
|
46
|
+
- [3. Razorpay Webhook Setup (Manual)](#3-razorpay-webhook-setup-manual)
|
|
47
|
+
- [Authentication (JWT for OAuth)](#authentication-jwt-for-oauth)
|
|
48
|
+
- [Callbacks](#callbacks)
|
|
49
|
+
- [Mandatory Callbacks](#mandatory-callbacks)
|
|
50
|
+
- [Optional Callbacks (Implement as needed)](#optional-callbacks-implement-as-needed)
|
|
51
|
+
- [Exposed Entities](#exposed-entities)
|
|
52
|
+
- [API Endpoints](#api-endpoints)
|
|
53
|
+
- [Incineration Concern](#incineration-concern)
|
|
54
|
+
- [Deprecated Patterns](#deprecated-patterns)
|
|
55
|
+
- [Development Environment Setup](#development-environment-setup-1)
|
|
56
|
+
- [Helper methods](#helper-methods)
|
|
57
|
+
- [Testing & Debugging](#testing--debugging)
|
|
58
|
+
- [Gotchas & Tips](#gotchas--tips)
|
|
59
|
+
- [Publishing](#publishing)
|
|
55
60
|
|
|
56
61
|
## Installation (Backend Engine)
|
|
57
62
|
|
|
58
|
-
Follow these steps to integrate the `neeto-payments-engine` into your host Rails
|
|
63
|
+
Follow these steps to integrate the `neeto-payments-engine` into your host Rails
|
|
64
|
+
application:
|
|
59
65
|
|
|
60
66
|
### 1. Add the Gem
|
|
67
|
+
|
|
61
68
|
Add the gem to your application's `Gemfile`:
|
|
69
|
+
|
|
62
70
|
```ruby
|
|
63
71
|
# Gemfile
|
|
64
72
|
source "NEETO_GEM_SERVER_URL" do
|
|
@@ -67,37 +75,51 @@ end
|
|
|
67
75
|
```
|
|
68
76
|
|
|
69
77
|
### 2. Install Gems
|
|
78
|
+
|
|
70
79
|
Run bundler to install the gem and its dependencies:
|
|
80
|
+
|
|
71
81
|
```bash
|
|
72
82
|
bundle install
|
|
73
83
|
```
|
|
74
84
|
|
|
75
85
|
### 3. Install Migrations
|
|
76
|
-
|
|
86
|
+
|
|
87
|
+
Copy the engine's database migrations into your host application. **This step is
|
|
88
|
+
crucial.**
|
|
89
|
+
|
|
77
90
|
```bash
|
|
78
91
|
bundle exec rails neeto_payments_engine:install:migrations
|
|
79
92
|
```
|
|
80
93
|
|
|
81
94
|
### 4. Run Migrations
|
|
95
|
+
|
|
82
96
|
Apply the migrations to your database:
|
|
97
|
+
|
|
83
98
|
```bash
|
|
84
99
|
bundle exec rails db:migrate
|
|
85
100
|
```
|
|
86
101
|
|
|
87
102
|
### 5. Mount the Engine
|
|
103
|
+
|
|
88
104
|
Add the engine's routes to your application's `config/routes.rb`:
|
|
105
|
+
|
|
89
106
|
```ruby
|
|
90
107
|
# config/routes.rb
|
|
91
108
|
mount NeetoPaymentsEngine::Engine, at: "/payments" # Or your preferred mount point
|
|
92
109
|
```
|
|
110
|
+
|
|
93
111
|
This makes the engine's API endpoints available, by default under `/payments`.
|
|
94
112
|
|
|
95
|
-
Once the installation is done, we have to do some configuration for the engine
|
|
113
|
+
Once the installation is done, we have to do some configuration for the engine
|
|
114
|
+
to work as intended. Please go through the whole README to complete the process
|
|
115
|
+
of setting up `neeto-payments-nano` in your host app.
|
|
96
116
|
|
|
97
117
|
## Configuration (Backend Engine)
|
|
98
118
|
|
|
99
119
|
### 1. Initializer
|
|
100
|
-
|
|
120
|
+
|
|
121
|
+
Create an initializer file `config/initializers/neeto_payments_engine.rb` to
|
|
122
|
+
configure the engine:
|
|
101
123
|
|
|
102
124
|
```ruby
|
|
103
125
|
# config/initializers/neeto_payments_engine.rb
|
|
@@ -113,10 +135,23 @@ NeetoPaymentsEngine.providers_holdable_class = {
|
|
|
113
135
|
upi: "Organization" # Typically Organization that owns the UPI VPA list
|
|
114
136
|
}
|
|
115
137
|
```
|
|
116
|
-
|
|
138
|
+
|
|
139
|
+
- **`providers_holdable_class`:** This hash maps each payment provider type
|
|
140
|
+
supported by the engine to the class name (as a String) of the model in your
|
|
141
|
+
host application that will "hold" or own the integration for that provider.
|
|
142
|
+
The engine uses polymorphic associations (`holdable_type`, `holdable_id`)
|
|
143
|
+
based on this setting to link payment provider accounts (like
|
|
144
|
+
`Stripe::Account`, `Integrations::Razorpay`) to your application's models.
|
|
145
|
+
Failing to configure this correctly will result in errors when the engine
|
|
146
|
+
attempts to find or create payment provider integrations. Example: In NeetoCal
|
|
147
|
+
each `User` can connect their own `stripe_standard` account, but in NeetoPay,
|
|
148
|
+
only one `stripe_standard` account can exist in an `Organization`.
|
|
117
149
|
|
|
118
150
|
### 2. Model Associations
|
|
119
|
-
|
|
151
|
+
|
|
152
|
+
Ensure the models specified in `providers_holdable_class` have the correct
|
|
153
|
+
`has_one` or `has_many` associations defined to link to the engine's integration
|
|
154
|
+
models. Adapt the following examples based on your configuration:
|
|
120
155
|
|
|
121
156
|
```ruby
|
|
122
157
|
# app/models/organization.rb (Example if Organization is a holdable)
|
|
@@ -152,7 +187,10 @@ class User < ApplicationRecord
|
|
|
152
187
|
end
|
|
153
188
|
```
|
|
154
189
|
|
|
155
|
-
Add associations to your **Payable** models (e.g., `Invoice`, `Booking`,
|
|
190
|
+
Add associations to your **Payable** models (e.g., `Invoice`, `Booking`,
|
|
191
|
+
`Meeting` - the item being paid for) - you don't need to add this until you
|
|
192
|
+
create your `Payable` models in your host app:
|
|
193
|
+
|
|
156
194
|
```ruby
|
|
157
195
|
# app/models/invoice.rb (Example Payable Model)
|
|
158
196
|
class Invoice < ApplicationRecord
|
|
@@ -165,125 +203,156 @@ end
|
|
|
165
203
|
```
|
|
166
204
|
|
|
167
205
|
### 3. Secrets and Credentials
|
|
168
|
-
Configure API keys and secrets securely using environment variables and secrets file.
|
|
169
|
-
|
|
170
|
-
**Essential ENV variables for `neeto-payments-engine`:**
|
|
171
|
-
|
|
172
|
-
* **`.env.development` (Commit this file):** Contains non-sensitive defaults, placeholders, or development-specific configurations.
|
|
173
|
-
```dotenv
|
|
174
|
-
# .env.development
|
|
175
|
-
|
|
176
|
-
# Base URL of your host application (adjust port if needed)
|
|
177
|
-
APP_URL='http://app.lvh.me:<APP_PORT>/'
|
|
178
206
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
STRIPE_CALLBACK_BASE_URL="https://connect.tunnelto.dev" # Or your tunnel URL
|
|
207
|
+
Configure API keys and secrets securely using environment variables and secrets
|
|
208
|
+
file.
|
|
182
209
|
|
|
183
|
-
|
|
184
|
-
RAZORPAY_CLIENT_SECRET="rzp_test_..." # Test Key ID can often be committed
|
|
185
|
-
RAZORPAY_CLIENT_ID="..." # OAuth Client ID is usually safe
|
|
186
|
-
RAZORPAY_WEBHOOK_SECRET=".." # webhook secret
|
|
187
|
-
RAZORPAY_CALLBACK_BASE_URL="https://connect.tunnelto.dev" # Or your tunnel URL
|
|
188
|
-
```
|
|
189
|
-
|
|
190
|
-
* **`.env.local` (Add this file to `.gitignore` - DO NOT COMMIT):** Contains sensitive API keys, secrets, and private keys. This file overrides values in `.env.development`.
|
|
191
|
-
|
|
192
|
-
```dotenv
|
|
193
|
-
# .env.local (DO NOT COMMIT THIS FILE)
|
|
194
|
-
|
|
195
|
-
# --- Stripe ---
|
|
196
|
-
STRIPE_SECRET_KEY="sk_test_..." # Your TEST Stripe Secret Key
|
|
197
|
-
STRIPE_WEBHOOK_SECRET="whsec_..." # Your TEST Stripe Webhook Signing Secret (from manual setup)
|
|
198
|
-
# Can find this from Stripe dashboard
|
|
199
|
-
STRIPE_PUBLISHABLE_KEY="pk_test_..."
|
|
200
|
-
# First sign up for Stripe Connect, then go to OAuth section of Stripe
|
|
201
|
-
# Connect to get the client id. You'd also have to register OAuth callback
|
|
202
|
-
# URI in that page.
|
|
203
|
-
STRIPE_CLIENT_ID="ca_..."
|
|
204
|
-
|
|
205
|
-
# --- Razorpay (if used) ---
|
|
206
|
-
RAZORPAY_KEY_SECRET="..." # Your TEST Razorpay Key Secret
|
|
207
|
-
RAZORPAY_WEBHOOK_SECRET="..." # Your TEST Razorpay Webhook Secret (you set this)
|
|
208
|
-
RAZORPAY_CLIENT_SECRET="..." # Your TEST Razorpay OAuth Client Secret
|
|
209
|
-
|
|
210
|
-
# --- JWT Keys ---
|
|
211
|
-
# Generate using: openssl genrsa -out private.pem 2048 && openssl rsa -in private.pem -pubout -out public.pem
|
|
212
|
-
# Copy the *entire* content including -----BEGIN...----- and -----END...----- lines.
|
|
213
|
-
CONNECT_PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----
|
|
214
|
-
...your private key content...
|
|
215
|
-
-----END RSA PRIVATE KEY-----"
|
|
216
|
-
CONNECT_PUBLIC_KEY="-----BEGIN PUBLIC KEY-----
|
|
217
|
-
...your public key content...
|
|
218
|
-
-----END PUBLIC KEY-----"
|
|
219
|
-
|
|
220
|
-
# --- Optional ---
|
|
221
|
-
# OPEN_EXCHANGE_RATE_API_KEY="your_actual_key" # Sensitive API key
|
|
222
|
-
```
|
|
223
|
-
|
|
224
|
-
**Loading Secrets:** Ensure these secrets are loaded into `Rails.application.secrets`. An example `config/secrets.yml` structure:
|
|
225
|
-
|
|
226
|
-
```yaml
|
|
227
|
-
# config/secrets.yml
|
|
228
|
-
default: &default
|
|
229
|
-
host: <%= ENV['APP_URL'] %>
|
|
230
|
-
stripe:
|
|
231
|
-
publishable_key: <%= ENV['STRIPE_PUBLISHABLE_KEY'] %>
|
|
232
|
-
secret_key: <%= ENV['STRIPE_SECRET_KEY'] %>
|
|
233
|
-
client_id: <%= ENV['STRIPE_CLIENT_ID'] %>
|
|
234
|
-
webhooks:
|
|
235
|
-
secret: <%= ENV['STRIPE_WEBHOOK_SECRET'] %>
|
|
236
|
-
razorpay:
|
|
237
|
-
key_id: <%= ENV["RAZORPAY_KEY_ID"] %>
|
|
238
|
-
key_secret: <%= ENV["RAZORPAY_KEY_SECRET"] %>
|
|
239
|
-
client_id: <%= ENV["RAZORPAY_CLIENT_ID"] %>
|
|
240
|
-
client_secret: <%= ENV["RAZORPAY_CLIENT_SECRET"] %>
|
|
241
|
-
oauth_callback_base_url: <%= ENV["RAZORPAY_CALLBACK_BASE_URL"] %>
|
|
242
|
-
webhook:
|
|
243
|
-
secret: <%= ENV["RAZORPAY_WEBHOOK_SECRET"] %>
|
|
244
|
-
jwt:
|
|
245
|
-
connect_private_key: <%= ENV['CONNECT_PRIVATE_KEY'].inspect %>
|
|
246
|
-
connect_public_key: <%= ENV['CONNECT_PUBLIC_KEY'].inspect %>
|
|
247
|
-
|
|
248
|
-
# Optional: If using Open Exchange Rates for currency conversion
|
|
249
|
-
open_exchange_rates:
|
|
250
|
-
api_key: <%= ENV["OPEN_EXCHANGE_RATE_API_KEY"] %>
|
|
251
|
-
# ... other secrets ...
|
|
252
|
-
|
|
253
|
-
development:
|
|
254
|
-
<<: *default
|
|
255
|
-
# Development specific overrides
|
|
256
|
-
|
|
257
|
-
production:
|
|
258
|
-
<<: *default
|
|
259
|
-
# Production specific overrides (use credentials!)
|
|
260
|
-
```
|
|
210
|
+
**Essential ENV variables for `neeto-payments-engine`:**
|
|
261
211
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
212
|
+
- **`.env.development` (Commit this file):** Contains non-sensitive defaults,
|
|
213
|
+
placeholders, or development-specific configurations.
|
|
214
|
+
|
|
215
|
+
```dotenv
|
|
216
|
+
# .env.development
|
|
217
|
+
|
|
218
|
+
# Base URL of your host application (adjust port if needed)
|
|
219
|
+
APP_URL='http://app.lvh.me:<APP_PORT>/'
|
|
220
|
+
|
|
221
|
+
# --- Stripe ---
|
|
222
|
+
# Base URL for OAuth callback (using tunnelto 'connect' subdomain for dev)
|
|
223
|
+
STRIPE_CALLBACK_BASE_URL="https://connect.tunnelto.dev" # Or your tunnel URL
|
|
224
|
+
|
|
225
|
+
# --- Razorpay (if used) ---
|
|
226
|
+
RAZORPAY_CLIENT_SECRET="rzp_test_..." # Test Key ID can often be committed
|
|
227
|
+
RAZORPAY_CLIENT_ID="..." # OAuth Client ID is usually safe
|
|
228
|
+
RAZORPAY_WEBHOOK_SECRET=".." # webhook secret
|
|
229
|
+
RAZORPAY_CALLBACK_BASE_URL="https://connect.tunnelto.dev" # Or your tunnel URL
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
- **`.env.local` (Add this file to `.gitignore` - DO NOT COMMIT):** Contains
|
|
233
|
+
sensitive API keys, secrets, and private keys. This file overrides values in
|
|
234
|
+
`.env.development`.
|
|
235
|
+
|
|
236
|
+
```dotenv
|
|
237
|
+
# .env.local (DO NOT COMMIT THIS FILE)
|
|
238
|
+
|
|
239
|
+
# --- Stripe ---
|
|
240
|
+
STRIPE_SECRET_KEY="sk_test_..." # Your TEST Stripe Secret Key
|
|
241
|
+
STRIPE_WEBHOOK_SECRET="whsec_..." # Your TEST Stripe Webhook Signing Secret (from manual setup)
|
|
242
|
+
# Can find this from Stripe dashboard
|
|
243
|
+
STRIPE_PUBLISHABLE_KEY="pk_test_..."
|
|
244
|
+
# First sign up for Stripe Connect, then go to OAuth section of Stripe
|
|
245
|
+
# Connect to get the client id. You'd also have to register OAuth callback
|
|
246
|
+
# URI in that page.
|
|
247
|
+
STRIPE_CLIENT_ID="ca_..."
|
|
248
|
+
|
|
249
|
+
# --- Razorpay (if used) ---
|
|
250
|
+
RAZORPAY_KEY_SECRET="..." # Your TEST Razorpay Key Secret
|
|
251
|
+
RAZORPAY_WEBHOOK_SECRET="..." # Your TEST Razorpay Webhook Secret (you set this)
|
|
252
|
+
RAZORPAY_CLIENT_SECRET="..." # Your TEST Razorpay OAuth Client Secret
|
|
253
|
+
|
|
254
|
+
# --- JWT Keys ---
|
|
255
|
+
# Generate using: openssl genrsa -out private.pem 2048 && openssl rsa -in private.pem -pubout -out public.pem
|
|
256
|
+
# Copy the *entire* content including -----BEGIN...----- and -----END...----- lines.
|
|
257
|
+
CONNECT_PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----
|
|
258
|
+
...your private key content...
|
|
259
|
+
-----END RSA PRIVATE KEY-----"
|
|
260
|
+
CONNECT_PUBLIC_KEY="-----BEGIN PUBLIC KEY-----
|
|
261
|
+
...your public key content...
|
|
262
|
+
-----END PUBLIC KEY-----"
|
|
263
|
+
|
|
264
|
+
# --- Optional ---
|
|
265
|
+
# OPEN_EXCHANGE_RATE_API_KEY="your_actual_key" # Sensitive API key
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
**Loading Secrets:** Ensure these secrets are loaded into
|
|
269
|
+
`Rails.application.secrets`. An example `config/secrets.yml` structure:
|
|
270
|
+
|
|
271
|
+
```yaml
|
|
272
|
+
# config/secrets.yml
|
|
273
|
+
default: &default
|
|
274
|
+
host: <%= ENV['APP_URL'] %>
|
|
275
|
+
stripe:
|
|
276
|
+
publishable_key: <%= ENV['STRIPE_PUBLISHABLE_KEY'] %>
|
|
277
|
+
secret_key: <%= ENV['STRIPE_SECRET_KEY'] %>
|
|
278
|
+
client_id: <%= ENV['STRIPE_CLIENT_ID'] %>
|
|
279
|
+
webhooks:
|
|
280
|
+
secret: <%= ENV['STRIPE_WEBHOOK_SECRET'] %>
|
|
281
|
+
razorpay:
|
|
282
|
+
key_id: <%= ENV["RAZORPAY_KEY_ID"] %>
|
|
283
|
+
key_secret: <%= ENV["RAZORPAY_KEY_SECRET"] %>
|
|
284
|
+
client_id: <%= ENV["RAZORPAY_CLIENT_ID"] %>
|
|
285
|
+
client_secret: <%= ENV["RAZORPAY_CLIENT_SECRET"] %>
|
|
286
|
+
oauth_callback_base_url: <%= ENV["RAZORPAY_CALLBACK_BASE_URL"] %>
|
|
287
|
+
webhook:
|
|
288
|
+
secret: <%= ENV["RAZORPAY_WEBHOOK_SECRET"] %>
|
|
289
|
+
jwt:
|
|
290
|
+
connect_private_key: <%= ENV['CONNECT_PRIVATE_KEY'].inspect %>
|
|
291
|
+
connect_public_key: <%= ENV['CONNECT_PUBLIC_KEY'].inspect %>
|
|
292
|
+
|
|
293
|
+
# Optional: If using Open Exchange Rates for currency conversion
|
|
294
|
+
open_exchange_rates:
|
|
295
|
+
api_key: <%= ENV["OPEN_EXCHANGE_RATE_API_KEY"] %>
|
|
296
|
+
# ... other secrets ...
|
|
297
|
+
|
|
298
|
+
development:
|
|
299
|
+
<<: *default
|
|
300
|
+
# Development specific overrides
|
|
301
|
+
|
|
302
|
+
production:
|
|
303
|
+
<<: *default
|
|
304
|
+
# Production specific overrides (use credentials!)
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
**Secrets Management:**
|
|
308
|
+
|
|
309
|
+
- **Development:** Use `.env.local` (which is typically gitignored) to store
|
|
310
|
+
sensitive keys locally.
|
|
311
|
+
- **Staging/Production:** Use environment variables managed by your deployment
|
|
312
|
+
platform (e.g., NeetoDeploy, Heroku Config Vars) or Rails encrypted
|
|
313
|
+
credentials. **Do not commit secrets directly into your repository.**
|
|
265
314
|
|
|
266
315
|
### 4. Stripe Connect Signup
|
|
267
|
-
|
|
316
|
+
|
|
317
|
+
You **must** sign up for Stripe Connect via the Stripe dashboard, even if you
|
|
318
|
+
only intend to use a Stripe Platform account. This registration enables the
|
|
319
|
+
necessary APIs for account management and OAuth flows used by the engine.
|
|
268
320
|
|
|
269
321
|
### 5. OAuth Callback URI Registration
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
322
|
+
|
|
323
|
+
Register the following callback URIs in your respective payment provider
|
|
324
|
+
dashboards:
|
|
325
|
+
|
|
326
|
+
- **Stripe:**
|
|
327
|
+
`https://<your_connect_subdomain_or_app_domain>/payments/api/v1/public/stripe/oauth/callback`
|
|
328
|
+
- `<your_connect_subdomain_or_app_domain>`: This should be the publicly
|
|
329
|
+
accessible URL that routes to your Rails app. In development, this is
|
|
330
|
+
typically your `tunnelto` URL using the `connect` subdomain (e.g.,
|
|
331
|
+
`https://connect.tunnelto.dev`). In production, it might be your main
|
|
332
|
+
application domain or a dedicated subdomain.
|
|
333
|
+
- **Razorpay:**
|
|
334
|
+
`https://<your_razorpay_oauth_base_url>/payments/api/v1/public/razorpay/oauth/callback`
|
|
335
|
+
- `<your_razorpay_oauth_base_url>`: This must match the value you configured
|
|
336
|
+
for `RAZORPAY_OAUTH_CALLBACK_BASE_URL`. Use the `connect` subdomain via
|
|
337
|
+
`tunnelto` in development.
|
|
275
338
|
|
|
276
339
|
## Frontend Integration
|
|
277
340
|
|
|
278
|
-
Integrate the React components provided by the
|
|
341
|
+
Integrate the React components provided by the
|
|
342
|
+
`@bigbinary/neeto-payments-frontend` package.
|
|
279
343
|
|
|
280
344
|
### 1. Install Frontend Package
|
|
345
|
+
|
|
281
346
|
```bash
|
|
282
347
|
yarn add @bigbinary/neeto-payments-frontend
|
|
283
348
|
```
|
|
284
349
|
|
|
285
350
|
### 2. Install Peer Dependencies
|
|
286
|
-
|
|
351
|
+
|
|
352
|
+
If the host app already includes all of the following peer deps, then you don't
|
|
353
|
+
have to install anything explicitly. Use the latest version of each of these
|
|
354
|
+
peer dependencies if you need to install:
|
|
355
|
+
|
|
287
356
|
```bash
|
|
288
357
|
# DO NOT INSTALL THE VERSIONS MENTIONED BELOW AS IT MIGHT BE OUTDATED.
|
|
289
358
|
# ALWAYS PREFER INSTALLING THE LATEST VERSIONS.
|
|
@@ -291,344 +360,398 @@ If the host app already includes all of the following peer deps, then you don't
|
|
|
291
360
|
# what's causing the breakage.
|
|
292
361
|
yarn add @babel/runtime@7.26.10 @bigbinary/neeto-cist@1.0.15 @bigbinary/neeto-commons-frontend@4.13.28 @bigbinary/neeto-editor@1.45.23 @bigbinary/neeto-filters-frontend@4.3.15 @bigbinary/neeto-icons@1.20.31 @bigbinary/neeto-molecules@3.16.1 @bigbinary/neetoui@8.2.75 @honeybadger-io/js@6.10.1 @honeybadger-io/react@6.1.25 @tailwindcss/container-queries@^0.1.1 @tanstack/react-query@5.59.20 @tanstack/react-query-devtools@5.59.20 antd@5.22.0 axios@1.8.2 buffer@^6.0.3 classnames@2.5.1 crypto-browserify@3.12.1 dompurify@^3.2.4 formik@2.4.6 https-browserify@1.0.0 i18next@22.5.1 js-logger@1.6.1 mixpanel-browser@^2.45.0 os-browserify@0.3.0 path-browserify@^1.0.1 qs@^6.11.2 ramda@0.29.0 react@18.2.0 react-dom@18.2.0 react-helmet@^6.1.0 react-i18next@12.3.1 react-router-dom@5.3.3 react-toastify@8.0.2 source-map-loader@4.0.1 stream-browserify@^3.0.0 stream-http@3.2.0 tailwindcss@3.4.14 tty-browserify@0.0.1 url@^0.11.0 util@^0.12.5 vm-browserify@1.1.2 yup@0.32.11 zustand@4.3.2
|
|
293
362
|
```
|
|
294
|
-
|
|
363
|
+
|
|
364
|
+
_Note: Carefully manage potential version conflicts with your host application._
|
|
295
365
|
|
|
296
366
|
### 3. Components
|
|
297
|
-
Import components into your React application as needed.
|
|
298
367
|
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
**Props**
|
|
302
|
-
- `feeId`: The unique identifier for the tax fee.
|
|
303
|
-
- `breadcrumbs`: Data for rendering the header breadcrumbs.
|
|
304
|
-
- `paymentUrl` _(optional)_: The URL of the pricing page to redirect users if payments are not enabled.
|
|
305
|
-
- `headerSize` _(optional)_: Specifies the size of the header. Default size is `small`
|
|
306
|
-
- `noDataHelpText` _(optional)_: Help text displayed when there is no data available.
|
|
307
|
-
- `onTaxesChange` _(optional)_: Callback function triggered after performing actions in the taxes dashboard.
|
|
308
|
-
- `titleHelpPopoverProps`_(optional)_: Data for the header help popover.
|
|
309
|
-
|
|
310
|
-
**Usage**
|
|
311
|
-
```jsx
|
|
312
|
-
import React from "react";
|
|
313
|
-
import { TaxesDashboard } from "@bigbinary/neeto-payments-frontend";
|
|
314
|
-
|
|
315
|
-
const App = () => {
|
|
316
|
-
return (
|
|
317
|
-
<TaxesDashboard
|
|
318
|
-
feeId={fee.id}
|
|
319
|
-
breadcrumbs={[{text: "Settings" ,link: routes.admin.form.settings}]}
|
|
320
|
-
/>
|
|
321
|
-
);
|
|
322
|
-
};
|
|
323
|
-
```
|
|
368
|
+
Import components into your React application as needed.
|
|
324
369
|
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
**Props**
|
|
328
|
-
- `dashboardKind`: Accepts either `connected` or `platform` (default: `platform`). Use `connected` to display payments related to split transfers. Use `platform` to display payments received from customers.
|
|
329
|
-
- `payableEntityColumns`: To specify the columns from the payable entity
|
|
330
|
-
which need to be additionally displayed. It is an optional prop that defaults to `[]` if not specified.
|
|
331
|
-
- `searchProps`: Allows specifying additional columns to be included in the search functionality. By default, only the `identifier` column from the payments table is searchable.
|
|
332
|
-
- `holdableIds`: Provide the holdable IDs for each payment provider when the holdable entity is not a `User` or `Organization` model.
|
|
333
|
-
|
|
334
|
-
**Usage**
|
|
335
|
-
```jsx
|
|
336
|
-
import React from "react";
|
|
337
|
-
import { PaymentsDashboard } from "@bigbinary/neeto-payments-frontend";
|
|
338
|
-
|
|
339
|
-
const App = () => {
|
|
340
|
-
|
|
341
|
-
// neeto-filters-engine search prop syntax.
|
|
342
|
-
const SEARCH_PROPS = {
|
|
343
|
-
node: "bookings:payable.meeting.name",
|
|
344
|
-
model: "Meeting",
|
|
345
|
-
placeholder: "Search by identifier or meeting name",
|
|
346
|
-
};
|
|
347
|
-
|
|
348
|
-
// Only required if the payment provider holdable modal is not a User or Organization
|
|
349
|
-
const holdableIds = { stripeStandard: formId, razorpay: formId }
|
|
350
|
-
|
|
351
|
-
const payableEntityColumns = {
|
|
352
|
-
title: "Submission",
|
|
353
|
-
dataIndex: ["payable", "id"],
|
|
354
|
-
key: "submission",
|
|
355
|
-
ellipsis: true,
|
|
356
|
-
width: "300px",,
|
|
357
|
-
},
|
|
370
|
+
1. `TaxesDashboard`
|
|
371
|
+
([source code](https://github.com/bigbinary/neeto-payments-nano/blob/main/app/javascript/src/components/TaxesDashboard/index.jsx))
|
|
358
372
|
|
|
359
|
-
|
|
360
|
-
<PaymentsDashboard
|
|
361
|
-
{...{ payableEntityColumns, holdableIds }}
|
|
362
|
-
searchProps={SEARCH_PROPS}
|
|
363
|
-
/>
|
|
364
|
-
);
|
|
365
|
-
};
|
|
366
|
-
```
|
|
367
|
-
3. `RefundsDashboard` ([source code](https://github.com/bigbinary/neeto-payments-nano/blob/main/app/javascript/src/components/RefundsDashboard/index.jsx))
|
|
373
|
+
**Props**
|
|
368
374
|
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
375
|
+
- `feeId`: The unique identifier for the tax fee.
|
|
376
|
+
- `breadcrumbs`: Data for rendering the header breadcrumbs.
|
|
377
|
+
- `paymentUrl` _(optional)_: The URL of the pricing page to redirect users if
|
|
378
|
+
payments are not enabled.
|
|
379
|
+
- `headerSize` _(optional)_: Specifies the size of the header. Default size
|
|
380
|
+
is `small`
|
|
381
|
+
- `noDataHelpText` _(optional)_: Help text displayed when there is no data
|
|
382
|
+
available.
|
|
383
|
+
- `onTaxesChange` _(optional)_: Callback function triggered after performing
|
|
384
|
+
actions in the taxes dashboard.
|
|
385
|
+
- `titleHelpPopoverProps`_(optional)_: Data for the header help popover.
|
|
373
386
|
|
|
374
387
|
**Usage**
|
|
375
|
-
```jsx
|
|
376
|
-
import React from "react";
|
|
377
|
-
import { SplitTransfersDashboard } from "@bigbinary/neeto-payments-frontend";
|
|
378
|
-
|
|
379
|
-
const App = () => {
|
|
380
|
-
|
|
381
|
-
// neeto-filters-engine search prop syntax.
|
|
382
|
-
const SEARCH_PROPS = {
|
|
383
|
-
node: "bookings:payable.meeting.name",
|
|
384
|
-
model: "Meeting",
|
|
385
|
-
placeholder: "Search by identifier or meeting name",
|
|
386
|
-
};
|
|
387
|
-
|
|
388
|
-
const payableEntityColumns = {
|
|
389
|
-
title: "Submission",
|
|
390
|
-
dataIndex: ["payable", "id"],
|
|
391
|
-
key: "submission",
|
|
392
|
-
ellipsis: true,
|
|
393
|
-
width: "300px",,
|
|
394
|
-
},
|
|
395
|
-
|
|
396
|
-
return (
|
|
397
|
-
<SplitTransfersDashboard
|
|
398
|
-
{...{ payableEntityColumns }}
|
|
399
|
-
searchProps={SEARCH_PROPS}
|
|
400
|
-
/>
|
|
401
|
-
);
|
|
402
|
-
};
|
|
403
|
-
```
|
|
404
388
|
|
|
405
|
-
|
|
389
|
+
```jsx
|
|
390
|
+
import React from "react";
|
|
391
|
+
import { TaxesDashboard } from "@bigbinary/neeto-payments-frontend";
|
|
392
|
+
|
|
393
|
+
const App = () => {
|
|
394
|
+
return (
|
|
395
|
+
<TaxesDashboard
|
|
396
|
+
feeId={fee.id}
|
|
397
|
+
breadcrumbs={[{ text: "Settings", link: routes.admin.form.settings }]}
|
|
398
|
+
/>
|
|
399
|
+
);
|
|
400
|
+
};
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
2. `PaymentsDashboard`
|
|
404
|
+
([source code](https://github.com/bigbinary/neeto-payments-nano/blob/main/app/javascript/src/components/PaymentsDashboard/index.jsx))
|
|
405
|
+
|
|
406
|
+
**Props**
|
|
407
|
+
|
|
408
|
+
- `dashboardKind`: Accepts either `connected` or `platform` (default:
|
|
409
|
+
`platform`). Use `connected` to display payments related to split
|
|
410
|
+
transfers. Use `platform` to display payments received from customers.
|
|
411
|
+
- `payableEntityColumns`: To specify the columns from the payable entity
|
|
412
|
+
which need to be additionally displayed. It is an optional prop that
|
|
413
|
+
defaults to `[]` if not specified.
|
|
414
|
+
- `searchProps`: Allows specifying additional columns to be included in the
|
|
415
|
+
search functionality. By default, only the `identifier` column from the
|
|
416
|
+
payments table is searchable.
|
|
417
|
+
- `holdableIds`: Provide the holdable IDs for each payment provider when the
|
|
418
|
+
holdable entity is not a `User` or `Organization` model.
|
|
406
419
|
|
|
407
|
-
**Props**
|
|
408
|
-
- `payableEntityColumns`: To specify the columns from the payable entity
|
|
409
|
-
which need to be additionally displayed. It is an optional prop that defaults to `[]` if not specified.
|
|
410
|
-
- `searchProps`: Allows specifying additional columns to be included in the search functionality. By default, only the `identifier` column from the payment splits table is searchable.
|
|
411
420
|
**Usage**
|
|
412
|
-
```jsx
|
|
413
|
-
import React from "react";
|
|
414
|
-
import { RefundsDashboard } from "@bigbinary/neeto-payments-frontend";
|
|
415
|
-
|
|
416
|
-
const App = () => {
|
|
417
|
-
|
|
418
|
-
// neeto-filters-engine search prop syntax.
|
|
419
|
-
const SEARCH_PROPS = {
|
|
420
|
-
node: "bookings:payable.meeting.name",
|
|
421
|
-
model: "Meeting",
|
|
422
|
-
placeholder: "Search by identifier or meeting name",
|
|
423
|
-
};
|
|
424
|
-
|
|
425
|
-
const payableEntityColumns = {
|
|
426
|
-
title: "Submission",
|
|
427
|
-
dataIndex: ["payable", "id"],
|
|
428
|
-
key: "submission",
|
|
429
|
-
ellipsis: true,
|
|
430
|
-
width: "300px",,
|
|
431
|
-
},
|
|
432
421
|
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
422
|
+
```jsx
|
|
423
|
+
import React from "react";
|
|
424
|
+
import { PaymentsDashboard } from "@bigbinary/neeto-payments-frontend";
|
|
425
|
+
|
|
426
|
+
const App = () => {
|
|
427
|
+
|
|
428
|
+
// neeto-filters-engine search prop syntax.
|
|
429
|
+
const SEARCH_PROPS = {
|
|
430
|
+
node: "bookings:payable.meeting.name",
|
|
431
|
+
model: "Meeting",
|
|
432
|
+
placeholder: "Search by identifier or meeting name",
|
|
433
|
+
};
|
|
434
|
+
|
|
435
|
+
// Only required if the payment provider holdable modal is not a User or Organization
|
|
436
|
+
const holdableIds = { stripeStandard: formId, razorpay: formId }
|
|
437
|
+
|
|
438
|
+
const payableEntityColumns = {
|
|
439
|
+
title: "Submission",
|
|
440
|
+
dataIndex: ["payable", "id"],
|
|
441
|
+
key: "submission",
|
|
442
|
+
ellipsis: true,
|
|
443
|
+
width: 300,
|
|
444
|
+
},
|
|
445
|
+
|
|
446
|
+
return (
|
|
447
|
+
<PaymentsDashboard
|
|
448
|
+
{...{ payableEntityColumns, holdableIds }}
|
|
449
|
+
searchProps={SEARCH_PROPS}
|
|
450
|
+
/>
|
|
451
|
+
);
|
|
452
|
+
};
|
|
453
|
+
```
|
|
454
|
+
|
|
455
|
+
3. `RefundsDashboard`
|
|
456
|
+
([source code](https://github.com/bigbinary/neeto-payments-nano/blob/main/app/javascript/src/components/RefundsDashboard/index.jsx))
|
|
457
|
+
|
|
458
|
+
**Props**
|
|
459
|
+
|
|
460
|
+
- `payableEntityColumns`: To specify the columns from the payable entity
|
|
461
|
+
which need to be additionally displayed. It is an optional prop that
|
|
462
|
+
defaults to `[]` if not specified.
|
|
463
|
+
- `searchProps`: Allows specifying additional columns to be included in the
|
|
464
|
+
search functionality. By default, only the `identifier` column from the
|
|
465
|
+
refunds table is searchable.
|
|
441
466
|
|
|
467
|
+
**Usage**
|
|
442
468
|
|
|
443
|
-
|
|
469
|
+
```jsx
|
|
470
|
+
import React from "react";
|
|
471
|
+
import { SplitTransfersDashboard } from "@bigbinary/neeto-payments-frontend";
|
|
472
|
+
|
|
473
|
+
const App = () => {
|
|
474
|
+
|
|
475
|
+
// neeto-filters-engine search prop syntax.
|
|
476
|
+
const SEARCH_PROPS = {
|
|
477
|
+
node: "bookings:payable.meeting.name",
|
|
478
|
+
model: "Meeting",
|
|
479
|
+
placeholder: "Search by identifier or meeting name",
|
|
480
|
+
};
|
|
481
|
+
|
|
482
|
+
const payableEntityColumns = {
|
|
483
|
+
title: "Submission",
|
|
484
|
+
dataIndex: ["payable", "id"],
|
|
485
|
+
key: "submission",
|
|
486
|
+
ellipsis: true,
|
|
487
|
+
width: 300,
|
|
488
|
+
},
|
|
489
|
+
|
|
490
|
+
return (
|
|
491
|
+
<SplitTransfersDashboard
|
|
492
|
+
{...{ payableEntityColumns }}
|
|
493
|
+
searchProps={SEARCH_PROPS}
|
|
494
|
+
/>
|
|
495
|
+
);
|
|
496
|
+
};
|
|
497
|
+
```
|
|
498
|
+
|
|
499
|
+
4. `SplitTransfersDashboard`
|
|
500
|
+
([source code](https://github.com/bigbinary/neeto-payments-nano/blob/main/app/javascript/src/components/SplitTransfersDashboard/index.jsx))
|
|
501
|
+
|
|
502
|
+
**Props**
|
|
503
|
+
|
|
504
|
+
- `payableEntityColumns`: To specify the columns from the payable entity
|
|
505
|
+
which need to be additionally displayed. It is an optional prop that
|
|
506
|
+
defaults to `[]` if not specified.
|
|
507
|
+
- `searchProps`: Allows specifying additional columns to be included in the
|
|
508
|
+
search functionality. By default, only the `identifier` column from the
|
|
509
|
+
payment splits table is searchable. **Usage**
|
|
510
|
+
|
|
511
|
+
```jsx
|
|
512
|
+
import React from "react";
|
|
513
|
+
import { RefundsDashboard } from "@bigbinary/neeto-payments-frontend";
|
|
514
|
+
|
|
515
|
+
const App = () => {
|
|
516
|
+
|
|
517
|
+
// neeto-filters-engine search prop syntax.
|
|
518
|
+
const SEARCH_PROPS = {
|
|
519
|
+
node: "bookings:payable.meeting.name",
|
|
520
|
+
model: "Meeting",
|
|
521
|
+
placeholder: "Search by identifier or meeting name",
|
|
522
|
+
};
|
|
523
|
+
|
|
524
|
+
const payableEntityColumns = {
|
|
525
|
+
title: "Submission",
|
|
526
|
+
dataIndex: ["payable", "id"],
|
|
527
|
+
key: "submission",
|
|
528
|
+
ellipsis: true,
|
|
529
|
+
width: 300,
|
|
530
|
+
},
|
|
531
|
+
|
|
532
|
+
return (
|
|
533
|
+
<RefundsDashboard
|
|
534
|
+
{...{ payableEntityColumns }}
|
|
535
|
+
searchProps={SEARCH_PROPS}
|
|
536
|
+
/>
|
|
537
|
+
);
|
|
538
|
+
};
|
|
539
|
+
```
|
|
540
|
+
|
|
541
|
+
5. `AccountsDashboard`
|
|
542
|
+
([source code](https://github.com/bigbinary/neeto-payments-nano/blob/main/app/javascript/src/components/AccountsDashboard/index.jsx))
|
|
444
543
|
|
|
445
544
|
**Usage**
|
|
446
|
-
```jsx
|
|
447
|
-
import React from "react";
|
|
448
|
-
import { AccountsDashboard } from "@bigbinary/neeto-payments-frontend";
|
|
449
545
|
|
|
450
|
-
|
|
546
|
+
```jsx
|
|
547
|
+
import React from "react";
|
|
548
|
+
import { AccountsDashboard } from "@bigbinary/neeto-payments-frontend";
|
|
451
549
|
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
550
|
+
const App = () => {
|
|
551
|
+
return <AccountsDashboard />;
|
|
552
|
+
};
|
|
553
|
+
```
|
|
455
554
|
|
|
456
|
-
6. `PayoutsDashboard`
|
|
555
|
+
6. `PayoutsDashboard`
|
|
556
|
+
([source code](https://github.com/bigbinary/neeto-payments-nano/blob/main/app/javascript/src/components/PayoutsDashboard/index.jsx))
|
|
457
557
|
|
|
458
|
-
|
|
459
|
-
- `isPlatformEnabled`: Indicates whether platform integration is enabled.
|
|
460
|
-
- `payoutsPageRoute`: The route used to display detailed payout information.
|
|
558
|
+
**Props**
|
|
461
559
|
|
|
560
|
+
- `isPlatformEnabled`: Indicates whether platform integration is enabled.
|
|
561
|
+
- `payoutsPageRoute`: The route used to display detailed payout information.
|
|
462
562
|
|
|
463
563
|
**Usage**
|
|
464
|
-
```jsx
|
|
465
|
-
import React from "react";
|
|
466
|
-
import { PayoutsDashboard } from "@bigbinary/neeto-payments-frontend";
|
|
467
|
-
|
|
468
|
-
const App = () => {
|
|
469
|
-
|
|
470
|
-
return (
|
|
471
|
-
<PayoutsDashboard
|
|
472
|
-
isPlatformEnabled={true}
|
|
473
|
-
payoutsPageRoute={routes.admin.payments.payouts.show}
|
|
474
|
-
/>
|
|
475
|
-
);
|
|
476
|
-
};
|
|
477
|
-
```
|
|
478
|
-
7. `RazorpayPaymentButton` ([source code](https://github.com/bigbinary/neeto-payments-nano/blob/main/app/javascript/src/components/RazorpayPaymentButton.jsx))
|
|
479
|
-
|
|
480
|
-
**Props**
|
|
481
|
-
- `label`: The button label. Defaults to `Pay`.
|
|
482
|
-
- `payableId`: The ID of the payable entity.
|
|
483
|
-
- `discountCode`: The discount code to be applied, if any.
|
|
484
|
-
- `email`: The customer's email address.
|
|
485
|
-
- `name`: The customer's name.
|
|
486
|
-
- `theme`: The theme configuration object.
|
|
487
|
-
- `payableType`: The type of the payable entity.
|
|
488
|
-
- `onBeforePayment`: A callback function triggered before the payment is initiated.
|
|
489
|
-
- `onSuccessfulPayment`: A callback function triggered after a successful payment.
|
|
490
|
-
- `onFailedPayment`: A callback function triggered after a failed payment.
|
|
491
|
-
|
|
492
|
-
**Usage**
|
|
493
|
-
```jsx
|
|
494
|
-
import React from "react";
|
|
495
|
-
import { RazorpayPaymentButton } from "@bigbinary/neeto-payments-frontend";
|
|
496
|
-
|
|
497
|
-
const App = () => {
|
|
498
|
-
|
|
499
|
-
return (
|
|
500
|
-
<RazorpayPaymentButton
|
|
501
|
-
email={customer.email}
|
|
502
|
-
name={customer.name}
|
|
503
|
-
discountCode={discountCode.code}
|
|
504
|
-
payableId={booking.id}
|
|
505
|
-
theme={meeting.theme}
|
|
506
|
-
/>
|
|
507
|
-
);
|
|
508
|
-
};
|
|
509
|
-
```
|
|
510
|
-
|
|
511
|
-
8. `ConfirmUpiPaymentButton` ([source code](https://github.com/bigbinary/neeto-payments-nano/blob/main/app/javascript/src/components/ConfirmUpiPaymentButton/index.jsx))
|
|
512
564
|
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
565
|
+
```jsx
|
|
566
|
+
import React from "react";
|
|
567
|
+
import { PayoutsDashboard } from "@bigbinary/neeto-payments-frontend";
|
|
568
|
+
|
|
569
|
+
const App = () => {
|
|
570
|
+
return (
|
|
571
|
+
<PayoutsDashboard
|
|
572
|
+
isPlatformEnabled={true}
|
|
573
|
+
payoutsPageRoute={routes.admin.payments.payouts.show}
|
|
574
|
+
/>
|
|
575
|
+
);
|
|
576
|
+
};
|
|
577
|
+
```
|
|
578
|
+
|
|
579
|
+
7. `RazorpayPaymentButton`
|
|
580
|
+
([source code](https://github.com/bigbinary/neeto-payments-nano/blob/main/app/javascript/src/components/RazorpayPaymentButton.jsx))
|
|
581
|
+
|
|
582
|
+
**Props**
|
|
583
|
+
|
|
584
|
+
- `label`: The button label. Defaults to `Pay`.
|
|
585
|
+
- `payableId`: The ID of the payable entity.
|
|
586
|
+
- `discountCode`: The discount code to be applied, if any.
|
|
587
|
+
- `email`: The customer's email address.
|
|
588
|
+
- `name`: The customer's name.
|
|
589
|
+
- `theme`: The theme configuration object.
|
|
590
|
+
- `payableType`: The type of the payable entity.
|
|
591
|
+
- `onBeforePayment`: A callback function triggered before the payment is
|
|
592
|
+
initiated.
|
|
593
|
+
- `onSuccessfulPayment`: A callback function triggered after a successful
|
|
594
|
+
payment.
|
|
595
|
+
- `onFailedPayment`: A callback function triggered after a failed payment.
|
|
518
596
|
|
|
519
|
-
|
|
597
|
+
**Usage**
|
|
520
598
|
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
599
|
+
```jsx
|
|
600
|
+
import React from "react";
|
|
601
|
+
import { RazorpayPaymentButton } from "@bigbinary/neeto-payments-frontend";
|
|
602
|
+
|
|
603
|
+
const App = () => {
|
|
604
|
+
return (
|
|
605
|
+
<RazorpayPaymentButton
|
|
606
|
+
email={customer.email}
|
|
607
|
+
name={customer.name}
|
|
608
|
+
discountCode={discountCode.code}
|
|
609
|
+
payableId={booking.id}
|
|
610
|
+
theme={meeting.theme}
|
|
611
|
+
/>
|
|
612
|
+
);
|
|
613
|
+
};
|
|
614
|
+
```
|
|
615
|
+
|
|
616
|
+
8. `ConfirmUpiPaymentButton`
|
|
617
|
+
([source code](https://github.com/bigbinary/neeto-payments-nano/blob/main/app/javascript/src/components/ConfirmUpiPaymentButton/index.jsx))
|
|
618
|
+
|
|
619
|
+
**Props**
|
|
620
|
+
|
|
621
|
+
- `vpas`: A list of UPI IDs presented for the user to select during
|
|
622
|
+
confirmation.
|
|
623
|
+
- `identifier`: The unique identifier associated with the payment.
|
|
624
|
+
- `paymentId`: The ID of the payment.
|
|
625
|
+
- `onSuccess`: A callback function triggered upon successful confirmation.
|
|
524
626
|
|
|
525
|
-
|
|
627
|
+
**Usage**
|
|
526
628
|
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
629
|
+
```jsx
|
|
630
|
+
import React from "react";
|
|
631
|
+
import { ConfirmUpiPaymentButton } from "@bigbinary/neeto-payments-frontend";
|
|
632
|
+
|
|
633
|
+
const App = () => {
|
|
634
|
+
return (
|
|
635
|
+
<ConfirmUpiPaymentButton
|
|
636
|
+
identifier={payment.identifier}
|
|
637
|
+
payableId={payment.payableId}
|
|
638
|
+
paymentId={payment.paymentId}
|
|
639
|
+
vpas={meeting.fee.vpas}
|
|
640
|
+
/>
|
|
641
|
+
);
|
|
642
|
+
};
|
|
643
|
+
```
|
|
537
644
|
|
|
538
645
|
### 4. Hooks
|
|
539
646
|
|
|
540
|
-
1. `useStripePromise`
|
|
647
|
+
1. `useStripePromise`
|
|
648
|
+
([source code](https://github.com/bigbinary/neeto-payments-nano/blob/main/app/javascript/src/hooks/useStripePromise.js))
|
|
649
|
+
|
|
650
|
+
This hook can used to provide the value for the `stripe` prop of the Stripe
|
|
651
|
+
`Element` component.
|
|
541
652
|
|
|
542
|
-
|
|
653
|
+
**Usage**
|
|
543
654
|
|
|
544
|
-
|
|
655
|
+
```jsx
|
|
656
|
+
import React from "react";
|
|
657
|
+
import { useStripePromise } from "@bigbinary/neeto-payments-frontend";
|
|
545
658
|
|
|
546
|
-
|
|
547
|
-
import React from "react";
|
|
548
|
-
import { useStripePromise } from "@bigbinary/neeto-payments-frontend";
|
|
659
|
+
const App = () => {
|
|
549
660
|
|
|
550
|
-
|
|
661
|
+
const stripePromise = useStripePromise({
|
|
662
|
+
stripePlatformAccount // The integration object for the Stripe platform account. No value is required if the platform account integration is not configured.
|
|
551
663
|
|
|
552
|
-
|
|
553
|
-
|
|
664
|
+
stripeAccountIdentifier: paymentRecipient?.stripeConnectedAccount?.identifier, // Stripe Standard account integration identifier
|
|
665
|
+
});
|
|
554
666
|
|
|
555
|
-
|
|
556
|
-
|
|
667
|
+
return (
|
|
668
|
+
<Elements stripe={stripePromise}>
|
|
669
|
+
</Elements>
|
|
670
|
+
);
|
|
671
|
+
};
|
|
672
|
+
```
|
|
557
673
|
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
</Elements>
|
|
561
|
-
);
|
|
562
|
-
};
|
|
563
|
-
```
|
|
564
|
-
2. `useRazorpayPayment` ([source code](https://github.com/bigbinary/neeto-payments-nano/blob/main/app/javascript/src/hooks/useRazorpayPayment.js))
|
|
674
|
+
2. `useRazorpayPayment`
|
|
675
|
+
([source code](https://github.com/bigbinary/neeto-payments-nano/blob/main/app/javascript/src/hooks/useRazorpayPayment.js))
|
|
565
676
|
|
|
566
|
-
|
|
677
|
+
This hook returns a function that can be used to initiate a Razorpay payment.
|
|
678
|
+
Use it only if you want to trigger the payment flow through a custom button
|
|
679
|
+
in the host application.
|
|
567
680
|
|
|
681
|
+
**Usage**
|
|
568
682
|
|
|
569
|
-
|
|
683
|
+
```jsx
|
|
684
|
+
import React from "react";
|
|
685
|
+
import { useRazorpayPayment } from "@bigbinary/neeto-payments-frontend";
|
|
686
|
+
|
|
687
|
+
const App = () => {
|
|
688
|
+
const { makePayment } = useRazorpayPayment({
|
|
689
|
+
payableId, // The ID of the payable entity.
|
|
690
|
+
onBeforePayment, // A callback function triggered before the payment is initiated.
|
|
691
|
+
onSuccessfulPayment, // A callback function triggered after a successful payment.
|
|
692
|
+
onFailedPayment, // A callback function triggered after a failed payment.
|
|
693
|
+
customAmount, // The custom amount value to be used when a fee of type `range` or `variable` is configured.
|
|
694
|
+
});
|
|
695
|
+
};
|
|
696
|
+
```
|
|
570
697
|
|
|
571
|
-
|
|
572
|
-
import React from "react";
|
|
573
|
-
import { useRazorpayPayment } from "@bigbinary/neeto-payments-frontend";
|
|
698
|
+
### 5. constants
|
|
574
699
|
|
|
575
|
-
|
|
700
|
+
1. CURRENCY_OPTIONS
|
|
576
701
|
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
onBeforePayment, // A callback function triggered before the payment is initiated.
|
|
580
|
-
onSuccessfulPayment, // A callback function triggered after a successful payment.
|
|
581
|
-
onFailedPayment, // A callback function triggered after a failed payment.
|
|
582
|
-
customAmount, // The custom amount value to be used when a fee of type `range` or `variable` is configured.
|
|
702
|
+
A list of supported currencies in `{ value, label }` format. This can be used
|
|
703
|
+
as options for the NeetoUI `Select` component.
|
|
583
704
|
|
|
584
|
-
|
|
705
|
+
### 6. utils
|
|
585
706
|
|
|
586
|
-
|
|
587
|
-
```
|
|
707
|
+
1. `getFormattedAmount({ amount, taxes=[], isTaxEnabled = false})`
|
|
588
708
|
|
|
589
|
-
|
|
709
|
+
Returns the final amount as a **string**, truncated to **2 decimal places**,
|
|
710
|
+
after applying taxes if `isTaxEnabled` is set to `true` and taxes are
|
|
711
|
+
present.
|
|
590
712
|
|
|
591
|
-
|
|
713
|
+
#### Parameter keys
|
|
592
714
|
|
|
593
|
-
|
|
715
|
+
- `amount`: The base amount to which taxes may be applied.
|
|
716
|
+
- `taxes`: List of tax objects with `value` and `kind` (`"percentage"` or
|
|
717
|
+
`"fixed"`). Defaults is `[]`
|
|
718
|
+
- `isTaxEnabled` : Whether taxes should be applied. Defaults to `false`.
|
|
594
719
|
|
|
595
|
-
|
|
720
|
+
#### Returns:
|
|
596
721
|
|
|
597
|
-
|
|
722
|
+
- `string`: Final amount with up to 2 decimal places (truncated, not
|
|
723
|
+
rounded).
|
|
598
724
|
|
|
599
|
-
|
|
725
|
+
#### Example:
|
|
600
726
|
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
- `isTaxEnabled` : Whether taxes should be applied. Defaults to `false`.
|
|
727
|
+
```js
|
|
728
|
+
getFormattedAmount([{ kind: "percentage", value: 23 }], "39.59", true); // → "48.69"
|
|
729
|
+
```
|
|
605
730
|
|
|
606
|
-
|
|
607
|
-
- `string`: Final amount with up to 2 decimal places (truncated, not rounded).
|
|
731
|
+
1. `getFormattedTaxAmount({ amount, taxes=[]})`
|
|
608
732
|
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
getFormattedAmount([{ kind: "percentage", value: 23 }], "39.59", true); // → "48.69"
|
|
612
|
-
```
|
|
733
|
+
Returns the total tax amount as a **string**, truncated to **2 decimal
|
|
734
|
+
places**
|
|
613
735
|
|
|
614
|
-
|
|
736
|
+
#### Parameter keys
|
|
615
737
|
|
|
616
|
-
|
|
738
|
+
- `amount`: The base amount on which tax will be calculated
|
|
739
|
+
- `taxes`: List of tax objects with `value` and `kind` (`"percentage"` or
|
|
740
|
+
`"fixed"`). Defaults is `[]`
|
|
617
741
|
|
|
618
|
-
|
|
619
|
-
- `amount`: The base amount on which tax will be calculated
|
|
620
|
-
- `taxes`: List of tax objects with `value` and `kind` (`"percentage"` or `"fixed"`). Defaults is `[]`
|
|
742
|
+
#### Returns:
|
|
621
743
|
|
|
622
|
-
|
|
623
|
-
|
|
744
|
+
- `string`: Total tax amount with up to 2 decimal places (truncated, not
|
|
745
|
+
rounded).
|
|
624
746
|
|
|
625
|
-
|
|
626
|
-
```js
|
|
627
|
-
getFormattedTaxAmount([{ kind: "percentage", value: 23 }], "100"); // → "23.00"
|
|
628
|
-
```
|
|
747
|
+
#### Example:
|
|
629
748
|
|
|
749
|
+
```js
|
|
750
|
+
getFormattedTaxAmount([{ kind: "percentage", value: 23 }], "100"); // → "23.00"
|
|
751
|
+
```
|
|
630
752
|
|
|
631
753
|
### 6. Others
|
|
754
|
+
|
|
632
755
|
```
|
|
633
756
|
// Example: Payment Settings Page
|
|
634
757
|
import React, { useState, useEffect } from 'react';
|
|
@@ -725,12 +848,17 @@ export default PaymentSettingsPage;
|
|
|
725
848
|
```
|
|
726
849
|
|
|
727
850
|
### 4. API Calls - Specify Providers
|
|
728
|
-
|
|
851
|
+
|
|
852
|
+
When calling the `/api/v1/integrations` endpoint (or other APIs that might
|
|
853
|
+
implicitly check integrations), **you must pass the `providers` parameter**
|
|
854
|
+
listing only the providers your application uses. Omitting this or including
|
|
855
|
+
unused providers can lead to errors if the engine tries to load configurations
|
|
856
|
+
or associations that don't exist for your setup.
|
|
729
857
|
|
|
730
858
|
```javascript
|
|
731
859
|
// Correct: Fetching only Stripe Standard integration status
|
|
732
|
-
axios.get(
|
|
733
|
-
params: { providers: ["stripe_standard"] }
|
|
860
|
+
axios.get("/payments/api/v1/integrations", {
|
|
861
|
+
params: { providers: ["stripe_standard"] },
|
|
734
862
|
});
|
|
735
863
|
|
|
736
864
|
// Incorrect (if not using all providers): Might cause errors
|
|
@@ -739,23 +867,39 @@ axios.get('/payments/api/v1/integrations', {
|
|
|
739
867
|
|
|
740
868
|
## Core Concepts
|
|
741
869
|
|
|
742
|
-
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
-
|
|
747
|
-
|
|
870
|
+
- **Polymorphic Associations:** `payable` (what's being paid for), `accountable`
|
|
871
|
+
(the payment account, e.g., `Stripe::Account`), and `holdable` (owner of the
|
|
872
|
+
integration, e.g., `Organization`) link the engine to host app models
|
|
873
|
+
dynamically based on configuration.
|
|
874
|
+
- **Single Table Inheritance (STI):** Used for provider-specific models like
|
|
875
|
+
`Payments::Stripe`, `Payments::Razorpay`, `Integrations::Razorpay`.
|
|
876
|
+
- **Service Objects:** Encapsulate business logic (e.g.,
|
|
877
|
+
`Stripe::Payments::CreateService`).
|
|
878
|
+
- **Callbacks:** Host applications implement methods in
|
|
879
|
+
`NeetoPaymentsEngine::Callbacks` to customize behavior. See the
|
|
880
|
+
[Callbacks](#callbacks) section.
|
|
881
|
+
- **Webhooks:** Asynchronous events from providers update payment statuses. See
|
|
882
|
+
[Webhook Handling](#webhook-handling).
|
|
883
|
+
- **JWT Authentication:** Secures OAuth callback flows using RSA keys.
|
|
748
884
|
|
|
749
885
|
## Webhook Handling
|
|
750
886
|
|
|
751
|
-
-
|
|
752
|
-
-
|
|
753
|
-
-
|
|
887
|
+
- Webhooks are crucial for updating payment statuses asynchronously.
|
|
888
|
+
- Endpoints are available at `/payments/api/v1/public/<provider>/webhooks`.
|
|
889
|
+
- Signatures are verified using configured secrets.
|
|
754
890
|
|
|
755
891
|
### Development Environment Setup
|
|
756
892
|
|
|
757
893
|
#### 1. Tunneling
|
|
758
|
-
|
|
894
|
+
|
|
895
|
+
Use a tool like `ngrok` or `tunnelto` to expose your local development server to
|
|
896
|
+
the internet. The `connect` subdomain is reserved within Neeto for receiving
|
|
897
|
+
callbacks from third-party services. Refer to
|
|
898
|
+
[Using Tunnelto](https://neeto-engineering.neetokb.com/articles/using-tunnelto)
|
|
899
|
+
and
|
|
900
|
+
[Exposing Tunnelto Locally](https://neeto-engineering.neetokb.com/articles/writing-payment-tests)
|
|
901
|
+
for more details.
|
|
902
|
+
|
|
759
903
|
```bash
|
|
760
904
|
# Example using tunnelto
|
|
761
905
|
tunnelto --subdomain connect --port <YOUR_RAILS_APP_PORT>
|
|
@@ -763,38 +907,61 @@ tunnelto --subdomain connect --port <YOUR_RAILS_APP_PORT>
|
|
|
763
907
|
```
|
|
764
908
|
|
|
765
909
|
#### 2. Stripe Webhook Setup (Manual)
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
910
|
+
|
|
911
|
+
- Go to your **Stripe Test Dashboard** > Developers > Webhooks.
|
|
912
|
+
- Click "Add endpoint".
|
|
913
|
+
- Endpoint URL:
|
|
914
|
+
`https://connect.tunnelto.dev/payments/api/v1/public/stripe/webhooks` (replace
|
|
915
|
+
with your actual tunnel URL).
|
|
916
|
+
- Select events to listen to. Refer to `config/stripe/webhooks.yml` in the
|
|
917
|
+
engine for the required list (e.g., `payment_intent.succeeded`,
|
|
918
|
+
`charge.refunded`, `account.updated`).
|
|
919
|
+
- Click "Add endpoint".
|
|
920
|
+
- After creation, find the **Signing secret** (starts with `whsec_...`).
|
|
921
|
+
- **Copy this secret** and set it as the `STRIPE_WEBHOOK_SECRET` in your local
|
|
922
|
+
`.env.local` or development credentials.
|
|
923
|
+
- **Note:** The `neeto_payments_engine:stripe:webhooks:subscribe` rake task is
|
|
924
|
+
**not recommended** for development setup as it doesn't create the endpoint
|
|
925
|
+
and might show outdated secrets. Manual setup provides better control and
|
|
926
|
+
ensures you have the correct, current secret. Refer to the
|
|
927
|
+
[Official Stripe Webhook Documentation](https://docs.stripe.com/webhooks) for
|
|
928
|
+
more details.
|
|
774
929
|
|
|
775
930
|
#### 3. Razorpay Webhook Setup (Manual)
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
931
|
+
|
|
932
|
+
- Go to your **Razorpay Partners Dashboard**.
|
|
933
|
+
- Go to Applications
|
|
934
|
+
- Create a new application or open the existing application
|
|
935
|
+
- Add test webhook
|
|
936
|
+
- Webhook URL:
|
|
937
|
+
`https://connect.tunnelto.dev/payments/api/v1/public/razorpay/webhooks`
|
|
938
|
+
(replace with your tunnel URL).
|
|
939
|
+
- Set a **Secret** (create a strong random string).
|
|
940
|
+
- Select Active Events (e.g., `payment.authorized`, `payment.captured`,
|
|
941
|
+
`payment.failed`, `refund.processed`, `refund.failed`).
|
|
942
|
+
- Save the webhook.
|
|
943
|
+
- **Copy the Secret you set** and configure it as `RAZORPAY_WEBHOOK_SECRET` in
|
|
944
|
+
your local development environment.
|
|
785
945
|
|
|
786
946
|
## Authentication (JWT for OAuth)
|
|
787
947
|
|
|
788
|
-
-
|
|
789
|
-
-
|
|
790
|
-
|
|
791
|
-
-
|
|
948
|
+
- Secures the OAuth callback flow for connecting Stripe/Razorpay accounts.
|
|
949
|
+
- Uses RSA public/private keys (`CONNECT_PUBLIC_KEY`, `CONNECT_PRIVATE_KEY`)
|
|
950
|
+
configured in your secrets.
|
|
951
|
+
- The `ConnectLinkService` creates a short-lived JWT containing user context
|
|
952
|
+
when initiating the OAuth flow.
|
|
953
|
+
- The `JwtService` verifies the token signature using the public key upon
|
|
954
|
+
callback.
|
|
792
955
|
|
|
793
956
|
## Callbacks
|
|
794
957
|
|
|
795
|
-
Host applications **must** implement specific methods within a
|
|
958
|
+
Host applications **must** implement specific methods within a
|
|
959
|
+
`NeetoPaymentsEngine::Callbacks` module (e.g., in
|
|
960
|
+
`app/lib/neeto_payments_engine/callbacks.rb`) to tailor the engine's behavior to
|
|
961
|
+
their domain logic.
|
|
796
962
|
|
|
797
963
|
### Mandatory Callbacks
|
|
964
|
+
|
|
798
965
|
```ruby
|
|
799
966
|
# app/lib/neeto_payments_engine/callbacks.rb
|
|
800
967
|
module NeetoPaymentsEngine
|
|
@@ -814,6 +981,7 @@ end
|
|
|
814
981
|
```
|
|
815
982
|
|
|
816
983
|
### Optional Callbacks (Implement as needed)
|
|
984
|
+
|
|
817
985
|
```ruby
|
|
818
986
|
# app/lib/neeto_payments_engine/callbacks.rb
|
|
819
987
|
module NeetoPaymentsEngine
|
|
@@ -956,19 +1124,33 @@ end
|
|
|
956
1124
|
|
|
957
1125
|
## Exposed Entities
|
|
958
1126
|
|
|
959
|
-
The engine exposes various models, services, jobs, and tasks for integration and
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
1127
|
+
The engine exposes various models, services, jobs, and tasks for integration and
|
|
1128
|
+
extension:
|
|
1129
|
+
|
|
1130
|
+
- **Models:** `Payment`, `Fee`, `Refund`, `Split`, `Payments::Split`,
|
|
1131
|
+
`Stripe::Account`, `Stripe::PlatformAccount`, `Integration`, `Customer`,
|
|
1132
|
+
`PaymentMethod`, `Payout`, `WebhookEvent`, etc. Host apps primarily interact
|
|
1133
|
+
with these through ActiveRecord associations defined on their own models.
|
|
1134
|
+
- **Services:** Encapsulate core logic (e.g., `Stripe::Payments::CreateService`,
|
|
1135
|
+
`Razorpay::Accounts::CreateService`, `SplitTransfersFilterService`,
|
|
1136
|
+
`ExportCsvService`). While mostly used internally, filter and export services
|
|
1137
|
+
might be invoked directly or indirectly via controllers.
|
|
1138
|
+
- **Jobs:** Handle background tasks (`Stripe::WebhooksJob`,
|
|
1139
|
+
`StripePlatform::CreatePaymentSplitsJob`, `ExportCsvJob`,
|
|
1140
|
+
`CreatePaymentMethodDomainJob`). These are queued and processed by Sidekiq.
|
|
1141
|
+
- **Concerns:** Reusable modules (`Amountable`, `PaymentProcessable`, `Taxable`,
|
|
1142
|
+
`Stripe::Accountable`, `Refundable`). Mostly for internal engine use.
|
|
1143
|
+
- **Rake Tasks:**
|
|
1144
|
+
- `neeto_payments_engine:stripe:account:integrate`: Seeds a sample Stripe
|
|
1145
|
+
connected account. **Development/Testing only.**
|
|
1146
|
+
- `neeto_payments_engine:stripe:webhooks:subscribe`: **Do not rely on this for
|
|
1147
|
+
setup.** See [Webhook Handling](#webhook-handling) for manual setup
|
|
1148
|
+
instructions.
|
|
968
1149
|
|
|
969
1150
|
## API Endpoints
|
|
970
1151
|
|
|
971
|
-
The engine exposes several API endpoints under the configured mount path
|
|
1152
|
+
The engine exposes several API endpoints under the configured mount path
|
|
1153
|
+
(default `/payments`). Here are some key ones:
|
|
972
1154
|
|
|
973
1155
|
| Method | Path | Description | Authentication |
|
|
974
1156
|
| :----- | :------------------------------------------------ | :----------------------------------------------------------------------- | :------------- |
|
|
@@ -1015,20 +1197,39 @@ The engine exposes several API endpoints under the configured mount path (defaul
|
|
|
1015
1197
|
| POST | `/api/v1/public/razorpay/webhooks` | Razorpay webhook receiver endpoint. | Razorpay Sig. |
|
|
1016
1198
|
| POST | `/api/v1/public/stripe_platform/webhooks` | Stripe Platform webhook receiver endpoint. | Stripe Sig. |
|
|
1017
1199
|
|
|
1018
|
-
|
|
1200
|
+
_Note: "Host App Auth" means the endpoint relies on the host application's
|
|
1201
|
+
authentication (e.g., `authenticate_user!`). "JWT" means authentication relies
|
|
1202
|
+
on the JWT generated during the OAuth flow. "Open" means no authentication.
|
|
1203
|
+
"Stripe Sig." / "Razorpay Sig." means verification is done via webhook
|
|
1204
|
+
signatures._
|
|
1019
1205
|
|
|
1020
1206
|
## Incineration Concern
|
|
1021
1207
|
|
|
1022
|
-
If your host application uses `neeto-org-incineration-engine`, you need to
|
|
1023
|
-
|
|
1024
|
-
|
|
1208
|
+
If your host application uses `neeto-org-incineration-engine`, you need to
|
|
1209
|
+
integrate `NeetoPaymentsEngine` models correctly. The `NeetoPaymentsEngine::Fee`
|
|
1210
|
+
model often requires special handling as it might be associated with host
|
|
1211
|
+
application models (`feeable`).
|
|
1212
|
+
|
|
1213
|
+
1. **Initial Setup:** When first adding `neeto-payments-engine`, add
|
|
1214
|
+
`NeetoPaymentsEngine::Fee` to the `SKIPPED_MODELS` list in your host
|
|
1215
|
+
application's `IncinerableConcern` implementation (e.g., in
|
|
1216
|
+
`app/models/concerns/incinerable_concern.rb`). This prevents incineration
|
|
1217
|
+
errors before associations are correctly set up especially while running the
|
|
1218
|
+
whole test suite in host app.
|
|
1025
1219
|
```ruby
|
|
1026
1220
|
# In your host app's IncinerableConcern
|
|
1027
1221
|
# TODO: Incinerate Fee based on Entity later
|
|
1028
1222
|
SKIPPED_MODELS = ["NeetoActivitiesEngine::Activity", "NeetoPaymentsEngine::Fee"]
|
|
1029
1223
|
```
|
|
1030
|
-
2. **Define Associations Later(optional):** Add the
|
|
1031
|
-
|
|
1224
|
+
2. **Define Associations Later(optional):** Add the
|
|
1225
|
+
`has_one :fee, as: :feeable` association to your host application models
|
|
1226
|
+
that should have fees (e.g., `Product`, `ServicePlan`).
|
|
1227
|
+
3. **Update Incineration Logic(optional):** Once associations are defined,
|
|
1228
|
+
**remove** `"NeetoPaymentsEngine::Fee"` from `SKIPPED_MODELS`. You must then
|
|
1229
|
+
update your `IncinerableConcern.associated_models` method to include logic
|
|
1230
|
+
that correctly finds and targets `NeetoPaymentsEngine::Fee` records
|
|
1231
|
+
associated with the organization being incinerated, likely through the
|
|
1232
|
+
`feeable` association.
|
|
1032
1233
|
|
|
1033
1234
|
```ruby
|
|
1034
1235
|
# In your host app's IncinerableConcern
|
|
@@ -1048,30 +1249,50 @@ If your host application uses `neeto-org-incineration-engine`, you need to integ
|
|
|
1048
1249
|
# rest of the code
|
|
1049
1250
|
end
|
|
1050
1251
|
```
|
|
1051
|
-
|
|
1252
|
+
|
|
1253
|
+
Consult the engine's own `NeetoPaymentsEngine::IncinerableConcern`
|
|
1254
|
+
(`app/models/concerns/neeto_payments_engine/incinerable_concern.rb`) for the
|
|
1255
|
+
list of models it defines and how it targets them for deletion, to avoid
|
|
1256
|
+
duplication and ensure comprehensive cleanup.
|
|
1052
1257
|
|
|
1053
1258
|
## Deprecated Patterns
|
|
1054
1259
|
|
|
1055
|
-
Please be aware of the following deprecations and use the recommended
|
|
1260
|
+
Please be aware of the following deprecations and use the recommended
|
|
1261
|
+
alternatives:
|
|
1056
1262
|
|
|
1057
1263
|
1. **Configuration `holdable_class`:**
|
|
1058
|
-
|
|
1059
|
-
|
|
1264
|
+
|
|
1265
|
+
- **Deprecated:** The singular
|
|
1266
|
+
`NeetoPaymentsEngine.holdable_class = "ClassName"` configuration.
|
|
1267
|
+
- **Recommended:** Use the hash-based
|
|
1268
|
+
`NeetoPaymentsEngine.providers_holdable_class = { provider: "ClassName", ... }`
|
|
1269
|
+
to configure holdable models per provider. This provides more flexibility.
|
|
1060
1270
|
|
|
1061
1271
|
2. **Holdable-Scoped APIs:**
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1272
|
+
|
|
1273
|
+
- **Deprecated:** API endpoints previously located under
|
|
1274
|
+
`/api/v1/<provider>/holdables/:holdable_id/...` are deprecated.
|
|
1275
|
+
- **Recommended:** Use the newer API structure:
|
|
1276
|
+
- For general integration status: `/api/v1/integrations` (passing the
|
|
1277
|
+
`providers` param).
|
|
1278
|
+
- For provider-specific account management:
|
|
1279
|
+
`/api/v1/<provider>/accounts/...`.
|
|
1280
|
+
- For provider-specific resources like payouts:
|
|
1281
|
+
`/api/v1/<provider>/payouts/...`.
|
|
1067
1282
|
|
|
1068
1283
|
3. **`Charge` Model/Concept:**
|
|
1069
|
-
|
|
1070
|
-
|
|
1284
|
+
- **Deprecated:** Older versions might have referred to payment processing
|
|
1285
|
+
entities as `Charge`.
|
|
1286
|
+
- **Recommended:** The primary entity for payment processing is now
|
|
1287
|
+
`NeetoPaymentsEngine::Payment` (and its STI subclasses like
|
|
1288
|
+
`Payments::Stripe`). Use `Payment` in associations and logic. The concept
|
|
1289
|
+
of `Fee` (`NeetoPaymentsEngine::Fee`) represents the configuration of
|
|
1290
|
+
_what_ to charge, while `Payment` represents the actual transaction.
|
|
1071
1291
|
|
|
1072
1292
|
## Development Environment Setup
|
|
1073
1293
|
|
|
1074
|
-
To run the engine locally integrated with a host application, ensure the
|
|
1294
|
+
To run the engine locally integrated with a host application, ensure the
|
|
1295
|
+
following processes are running:
|
|
1075
1296
|
|
|
1076
1297
|
1. **Rails Server:** Starts the main web application.
|
|
1077
1298
|
```bash
|
|
@@ -1086,14 +1307,18 @@ To run the engine locally integrated with a host application, ensure the followi
|
|
|
1086
1307
|
```bash
|
|
1087
1308
|
bundle exec sidekiq -e development -C config/sidekiq.yml
|
|
1088
1309
|
```
|
|
1089
|
-
4. **Tunneling Service (for Webhooks/OAuth):** Exposes your local server to the
|
|
1310
|
+
4. **Tunneling Service (for Webhooks/OAuth):** Exposes your local server to the
|
|
1311
|
+
internet. Use the `connect` subdomain for OAuth callbacks.
|
|
1090
1312
|
```bash
|
|
1091
1313
|
# Using tunnelto (replace <YOUR_APP_PORT> with your Rails server port, e.g., 3000)
|
|
1092
1314
|
tunnelto --subdomain connect --port <YOUR_APP_PORT>
|
|
1093
1315
|
```
|
|
1094
|
-
|
|
1316
|
+
_Note: The `connect` subdomain is conventionally used across Neeto
|
|
1317
|
+
applications for receiving callbacks from third-party services like
|
|
1318
|
+
Stripe/Razorpay OAuth._
|
|
1095
1319
|
|
|
1096
1320
|
The `Procfile.dev.PAYMENTS` might look like this:
|
|
1321
|
+
|
|
1097
1322
|
```yaml
|
|
1098
1323
|
web: bundle exec rails s
|
|
1099
1324
|
vite: yarn dev
|
|
@@ -1102,62 +1327,94 @@ worker: bundle exec sidekiq -e development -C config/sidekiq.yml
|
|
|
1102
1327
|
```
|
|
1103
1328
|
|
|
1104
1329
|
## Helper methods
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1330
|
+
|
|
1331
|
+
- `currency_format_with_symbol` This helper method converts a currency code
|
|
1332
|
+
(like "INR" or "USD") into its corresponding symbol and returns the formatted
|
|
1333
|
+
amount with the symbol. Example $10.00.
|
|
1334
|
+
|
|
1335
|
+
Usage in host application:
|
|
1336
|
+
|
|
1337
|
+
- In general modules (e.g., Services or View helpers)
|
|
1338
|
+
|
|
1339
|
+
```ruby
|
|
1340
|
+
module ApplicationHelper
|
|
1341
|
+
include ::NeetoPaymentsEngine::CurrencyFormatHelper
|
|
1342
|
+
|
|
1343
|
+
def formatted_amount
|
|
1344
|
+
currency_format_with_symbol(payment&.amount, payment&.currency)
|
|
1117
1345
|
end
|
|
1118
|
-
|
|
1346
|
+
end
|
|
1347
|
+
```
|
|
1348
|
+
|
|
1349
|
+
- In mailers
|
|
1350
|
+
|
|
1351
|
+
1. Include the helper in your mailer:
|
|
1119
1352
|
|
|
1120
|
-
|
|
1353
|
+
```ruby
|
|
1354
|
+
class Packages::PurchaseConfirmedMailer < ApplicationMailer
|
|
1355
|
+
helper ::NeetoPaymentsEngine::CurrencyFormatHelper
|
|
1121
1356
|
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1357
|
+
def customer_email
|
|
1358
|
+
end
|
|
1359
|
+
end
|
|
1360
|
+
```
|
|
1126
1361
|
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
2. Use the method in the mailer view:
|
|
1132
|
-
```ruby
|
|
1133
|
-
<%= "#{currency_format_with_symbol(@purchase.payment&.amount,@purchase.payment&.currency)}" %>
|
|
1134
|
-
```
|
|
1362
|
+
2. Use the method in the mailer view:
|
|
1363
|
+
```ruby
|
|
1364
|
+
<%= "#{currency_format_with_symbol(@purchase.payment&.amount,@purchase.payment&.currency)}" %>
|
|
1365
|
+
```
|
|
1135
1366
|
|
|
1136
1367
|
## Testing & Debugging
|
|
1137
1368
|
|
|
1138
|
-
-
|
|
1139
|
-
|
|
1140
|
-
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
-
|
|
1148
|
-
-
|
|
1149
|
-
-
|
|
1150
|
-
-
|
|
1369
|
+
- **Dummy App:** Use the `test/dummy` app within the engine's repository for
|
|
1370
|
+
isolated testing.
|
|
1371
|
+
- **Test Helpers:** Utilize `NeetoPaymentsEngine::TestHelpers` (includes
|
|
1372
|
+
`HttpRequestHelpers`) for stubbing API calls to Stripe/Razorpay
|
|
1373
|
+
(`test/helpers/http_request_helpers/`).
|
|
1374
|
+
- **Stripe Test Cards:**
|
|
1375
|
+
- Valid Card: `4242 4242 4242 4242`
|
|
1376
|
+
- Declined Card: `4000 0000 0000 0002`
|
|
1377
|
+
- Expiry Date: Any future date (e.g., `12/30`)
|
|
1378
|
+
- CVC: Any 3 digits (e.g., `123`)
|
|
1379
|
+
- ZIP Code: Any valid ZIP (e.g., `12345`)
|
|
1380
|
+
- [More Stripe Test Cards](https://docs.stripe.com/testing)
|
|
1381
|
+
- **Razorpay Test Cards/UPI:** Refer to
|
|
1382
|
+
[Razorpay Testing Docs](https://razorpay.com/docs/payments/payments/test-card-details/).
|
|
1383
|
+
- **Logging:** Check Rails logs (`log/development.log`) for detailed output from
|
|
1384
|
+
the engine's `LogActivityHelper`.
|
|
1385
|
+
- **Provider Dashboards:** Use the Stripe and Razorpay **Test Mode** dashboards
|
|
1386
|
+
to view API logs, payment details, webhook attempts, and specific error
|
|
1387
|
+
messages.
|
|
1388
|
+
- **JWT Debugging:** Use tools like [jwt.io](https://jwt.io) to decode JWTs
|
|
1389
|
+
generated during OAuth flows. Paste the token and the **public key**
|
|
1390
|
+
(`CONNECT_PUBLIC_KEY`) to verify the signature and inspect the payload (check
|
|
1391
|
+
`exp` claim for expiry).
|
|
1151
1392
|
|
|
1152
1393
|
## Gotchas & Tips
|
|
1153
1394
|
|
|
1154
|
-
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1395
|
+
- **`providers_holdable_class` is Mandatory:** Forgetting to configure this in
|
|
1396
|
+
the initializer will lead to errors when the engine tries to find associated
|
|
1397
|
+
accounts.
|
|
1398
|
+
- **Specify `providers` in API Calls:** When calling `/api/v1/integrations`,
|
|
1399
|
+
always pass the `providers` param listing only the providers you actually use
|
|
1400
|
+
in your host app (e.g., `params: { providers: ["stripe_standard"] }`). Failing
|
|
1401
|
+
to do so might cause errors if the engine tries to load an unconfigured
|
|
1402
|
+
provider (like UPI).
|
|
1403
|
+
- **Stripe Connect Signup:** You _must_ complete the Stripe Connect signup
|
|
1404
|
+
process in your Stripe account, even for platform-only usage.
|
|
1405
|
+
- **Webhook Secrets in Dev:** Manually created webhook endpoint secrets from
|
|
1406
|
+
Stripe/Razorpay dashboards are the source of truth for development, not
|
|
1407
|
+
necessarily what rake tasks might print.
|
|
1408
|
+
- **JWT Key Security:** Treat your JWT private key with the same security as
|
|
1409
|
+
your API secret keys.
|
|
1410
|
+
- **Migration Order:** Always run
|
|
1411
|
+
`bundle exec rails neeto_payments_engine:install:migrations` _before_
|
|
1412
|
+
`db:migrate` when setting up or upgrading. We also need to run this rake task
|
|
1413
|
+
and migration after we run `./bin/setup` in the host app.
|
|
1160
1414
|
|
|
1161
1415
|
## Publishing
|
|
1162
1416
|
|
|
1163
|
-
For instructions on building and releasing the
|
|
1417
|
+
For instructions on building and releasing the
|
|
1418
|
+
`@bigbinary/neeto-payments-frontend` NPM package and the `neeto-payments-engine`
|
|
1419
|
+
Ruby gem, please refer to the internal guide:
|
|
1420
|
+
[Building and Releasing Packages](https://neeto-engineering.neetokb.com/articles/building-and-releasing-packages).
|