@bigbinary/neeto-payments-frontend 3.3.22 → 3.3.24
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 +1086 -254
- package/dist/ConfirmUpiPaymentButton.js +5 -2
- package/dist/ConfirmUpiPaymentButton.js.map +1 -1
- package/dist/PaymentsDashboard.js +1 -1
- package/dist/PaymentsDashboard.js.map +1 -1
- package/dist/RefundsDashboard.js +1 -1
- package/dist/RefundsDashboard.js.map +1 -1
- package/dist/SplitTransfersDashboard.js +1 -1
- package/dist/SplitTransfersDashboard.js.map +1 -1
- package/dist/cjs/ConfirmUpiPaymentButton.js +5 -2
- package/dist/cjs/ConfirmUpiPaymentButton.js.map +1 -1
- package/dist/cjs/PaymentsDashboard.js +1 -1
- package/dist/cjs/PaymentsDashboard.js.map +1 -1
- package/dist/cjs/RefundsDashboard.js +1 -1
- package/dist/cjs/RefundsDashboard.js.map +1 -1
- package/dist/cjs/SplitTransfersDashboard.js +1 -1
- package/dist/cjs/SplitTransfersDashboard.js.map +1 -1
- package/package.json +13 -13
package/README.md
CHANGED
|
@@ -1,295 +1,1127 @@
|
|
|
1
1
|
# neeto-payments-nano
|
|
2
|
-
The `neeto-payments-nano` manages payments across neeto products. The nano exports the `@bigbinary/neeto-payments-frontend` NPM package and `neeto-payments-engine` Rails engine for development.
|
|
3
|
-
|
|
4
|
-
# Contents
|
|
5
|
-
1. [Development with Host Application](#development-with-host-application)
|
|
6
|
-
- [Engine](#engine)
|
|
7
|
-
- [Installation](#installation)
|
|
8
|
-
- [Usage](#usage)
|
|
9
|
-
- [Frontend package](#frontend-package)
|
|
10
|
-
- [Installation](#installation-1)
|
|
11
|
-
- [Instructions for development](#instructions-for-development)
|
|
12
|
-
- [Components](#components)
|
|
13
|
-
- [Hooks](#hooks)
|
|
14
|
-
- [Functions](#functions)
|
|
15
|
-
2. [Instructions for Publishing](#instructions-for-publishing)
|
|
16
|
-
|
|
17
|
-
# Development with Host Application
|
|
18
|
-
## Engine
|
|
19
|
-
The engine is used to manage payments across neeto products.
|
|
20
|
-
|
|
21
|
-
### Installation
|
|
22
|
-
1. Add this line to your application's Gemfile:
|
|
23
|
-
```ruby
|
|
24
|
-
source "NEETO_GEM_SERVER_URL" do
|
|
25
|
-
# ..existing gems
|
|
26
2
|
|
|
27
|
-
|
|
28
|
-
|
|
3
|
+
The `neeto-payments-nano` is a comprehensive payment processing solution designed for the Neeto ecosystem. Implemented as a Ruby on Rails engine with associated React frontend components (`@bigbinary/neeto-payments-frontend`), it provides a unified interface for managing payments across different providers, abstracting away provider-specific complexities.
|
|
4
|
+
|
|
5
|
+
This engine enables host applications to:
|
|
6
|
+
|
|
7
|
+
- Integrate with multiple payment providers (Stripe, Razorpay, UPI).
|
|
8
|
+
- Process payments.
|
|
9
|
+
- Handle payment splits for marketplace scenarios (Stripe).
|
|
10
|
+
- Manage refunds.
|
|
11
|
+
- Store and reuse payment methods securely (optional).
|
|
12
|
+
- Configure fee structures and apply taxes/discounts.
|
|
13
|
+
- Provide administrative dashboards for monitoring transactions, refunds, payouts, and accounts.
|
|
14
|
+
|
|
15
|
+
# Table of Contents
|
|
16
|
+
|
|
17
|
+
- [Installation (Backend Engine)](#installation-backend-engine)
|
|
18
|
+
- [1. Add the Gem](#1-add-the-gem)
|
|
19
|
+
- [2. Install Gems](#2-install-gems)
|
|
20
|
+
- [3. Install Migrations](#3-install-migrations)
|
|
21
|
+
- [4. Run Migrations](#4-run-migrations)
|
|
22
|
+
- [5. Mount the Engine](#5-mount-the-engine)
|
|
23
|
+
- [Configuration (Backend Engine)](#configuration-backend-engine)
|
|
24
|
+
- [1. Initializer](#1-initializer)
|
|
25
|
+
- [2. Model Associations](#2-model-associations)
|
|
26
|
+
- [3. Secrets and Credentials](#3-secrets-and-credentials)
|
|
27
|
+
- [4. Stripe Connect Signup](#4-stripe-connect-signup)
|
|
28
|
+
- [5. OAuth Callback URI Registration](#5-oauth-callback-uri-registration)
|
|
29
|
+
- [Frontend Integration](#frontend-integration)
|
|
30
|
+
- [1. Install Frontend Package](#1-install-frontend-package)
|
|
31
|
+
- [2. Install Peer Dependencies](#2-install-peer-dependencies)
|
|
32
|
+
- [3. Components](#3-components)
|
|
33
|
+
- [4. Hooks](#4-hooks)
|
|
34
|
+
- [5. Constants](#5-constants)
|
|
35
|
+
- [6. API Calls - Specify Providers](#4-api-calls---specify-providers)
|
|
36
|
+
- [Core Concepts](#core-concepts)
|
|
37
|
+
- [Webhook Handling](#webhook-handling)
|
|
38
|
+
- [Development Environment Setup](#development-environment-setup)
|
|
39
|
+
- [1. Tunneling](#1-tunneling)
|
|
40
|
+
- [2. Stripe Webhook Setup (Manual)](#2-stripe-webhook-setup-manual)
|
|
41
|
+
- [3. Razorpay Webhook Setup (Manual)](#3-razorpay-webhook-setup-manual)
|
|
42
|
+
- [Authentication (JWT for OAuth)](#authentication-jwt-for-oauth)
|
|
43
|
+
- [Callbacks](#callbacks)
|
|
44
|
+
- [Mandatory Callbacks](#mandatory-callbacks)
|
|
45
|
+
- [Optional Callbacks (Implement as needed)](#optional-callbacks-implement-as-needed)
|
|
46
|
+
- [Exposed Entities](#exposed-entities)
|
|
47
|
+
- [API Endpoints](#api-endpoints)
|
|
48
|
+
- [Incineration Concern](#incineration-concern)
|
|
49
|
+
- [Deprecated Patterns](#deprecated-patterns)
|
|
50
|
+
- [Development Environment Setup](#development-environment-setup-1)
|
|
51
|
+
- [Helper methods](#helper-methods)
|
|
52
|
+
- [Testing & Debugging](#testing--debugging)
|
|
53
|
+
- [Gotchas & Tips](#gotchas--tips)
|
|
54
|
+
- [Publishing](#publishing)
|
|
55
|
+
|
|
56
|
+
## Installation (Backend Engine)
|
|
57
|
+
|
|
58
|
+
Follow these steps to integrate the `neeto-payments-engine` into your host Rails application:
|
|
59
|
+
|
|
60
|
+
### 1. Add the Gem
|
|
61
|
+
Add the gem to your application's `Gemfile`:
|
|
62
|
+
```ruby
|
|
63
|
+
# Gemfile
|
|
64
|
+
source "NEETO_GEM_SERVER_URL" do
|
|
65
|
+
gem "neeto-payments-engine"
|
|
66
|
+
end
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### 2. Install Gems
|
|
70
|
+
Run bundler to install the gem and its dependencies:
|
|
71
|
+
```bash
|
|
72
|
+
bundle install
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### 3. Install Migrations
|
|
76
|
+
Copy the engine's database migrations into your host application. **This step is crucial.**
|
|
77
|
+
```bash
|
|
78
|
+
bundle exec rails neeto_payments_engine:install:migrations
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### 4. Run Migrations
|
|
82
|
+
Apply the migrations to your database:
|
|
83
|
+
```bash
|
|
84
|
+
bundle exec rails db:migrate
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### 5. Mount the Engine
|
|
88
|
+
Add the engine's routes to your application's `config/routes.rb`:
|
|
89
|
+
```ruby
|
|
90
|
+
# config/routes.rb
|
|
91
|
+
mount NeetoPaymentsEngine::Engine, at: "/payments" # Or your preferred mount point
|
|
92
|
+
```
|
|
93
|
+
This makes the engine's API endpoints available, by default under `/payments`.
|
|
94
|
+
|
|
95
|
+
Once the installation is done, we have to do some configuration for the engine to work as intended. Please go through the whole README to complete the process of setting up `neeto-payments-nano` in your host app.
|
|
96
|
+
|
|
97
|
+
## Configuration (Backend Engine)
|
|
98
|
+
|
|
99
|
+
### 1. Initializer
|
|
100
|
+
Create an initializer file `config/initializers/neeto_payments_engine.rb` to configure the engine:
|
|
101
|
+
|
|
102
|
+
```ruby
|
|
103
|
+
# config/initializers/neeto_payments_engine.rb
|
|
104
|
+
NeetoPaymentsEngine.is_card_preserving_enabled = false # Set to true to enable saving payment methods
|
|
105
|
+
|
|
106
|
+
# IMPORTANT: Configure which model holds the payment integration for each provider.
|
|
107
|
+
# Replace "Organization" or "User" with your actual model names.
|
|
108
|
+
# This configuration is MANDATORY.
|
|
109
|
+
NeetoPaymentsEngine.providers_holdable_class = {
|
|
110
|
+
stripe_standard: "Organization", # e.g., Organization or User that owns the Stripe Standard account
|
|
111
|
+
stripe_platform: "Organization", # Typically Organization that owns the Stripe Platform account
|
|
112
|
+
razorpay: "Organization", # e.g., Organization or User that owns the Razorpay account
|
|
113
|
+
upi: "Organization" # Typically Organization that owns the UPI VPA list
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
- **`providers_holdable_class`:** This hash maps each payment provider type supported by the engine to the class name (as a String) of the model in your host application that will "hold" or own the integration for that provider. The engine uses polymorphic associations (`holdable_type`, `holdable_id`) based on this setting to link payment provider accounts (like `Stripe::Account`, `Integrations::Razorpay`) to your application's models. Failing to configure this correctly will result in errors when the engine attempts to find or create payment provider integrations. Example: In NeetoCal each `User` can connect their own `stripe_standard` account, but in NeetoPay, only one `stripe_standard` account can exist in an `Organization`.
|
|
117
|
+
|
|
118
|
+
### 2. Model Associations
|
|
119
|
+
Ensure the models specified in `providers_holdable_class` have the correct `has_one` or `has_many` associations defined to link to the engine's integration models. Adapt the following examples based on your configuration:
|
|
120
|
+
|
|
121
|
+
```ruby
|
|
122
|
+
# app/models/organization.rb (Example if Organization is a holdable)
|
|
123
|
+
class Organization < ApplicationRecord
|
|
124
|
+
# If Organization holds Stripe Standard Connect account(s)
|
|
125
|
+
has_one :stripe_connected_account, -> { where(payment_provider: :stripe_standard) }, class_name: "::NeetoPaymentsEngine::Stripe::Account", as: :holdable, dependent: :destroy
|
|
126
|
+
# OR if one Org can hold many standard accounts (less common)
|
|
127
|
+
# has_many :stripe_connected_accounts, -> { where(payment_provider: :stripe_standard) }, class_name: "::NeetoPaymentsEngine::Stripe::Account", as: :holdable, dependent: :destroy
|
|
128
|
+
|
|
129
|
+
# We have to add this association no matter what because
|
|
130
|
+
# it's internally required by the engine. Change the parent association
|
|
131
|
+
# to Organization/User/Whatever depending on your host app.
|
|
132
|
+
has_one :stripe_platform_account, class_name: "::NeetoPaymentsEngine::Stripe::PlatformAccount", inverse_of: :organization, dependent: :destroy
|
|
133
|
+
|
|
134
|
+
# If Organization holds the Razorpay account
|
|
135
|
+
has_one :razorpay_integration, -> { where(payment_provider: :razorpay) }, class_name: "::NeetoPaymentsEngine::Integration", as: :holdable, dependent: :destroy
|
|
136
|
+
|
|
137
|
+
# If Organization holds the UPI VPAs list
|
|
138
|
+
has_one :upi_integration, -> { where(payment_provider: :eupi) }, class_name: "::NeetoPaymentsEngine::Integration", as: :holdable, dependent: :destroy
|
|
139
|
+
|
|
140
|
+
# General association (optional, can be useful)
|
|
141
|
+
has_many :payment_integrations, class_name: "::NeetoPaymentsEngine::Integration", as: :holdable, dependent: :destroy
|
|
142
|
+
|
|
143
|
+
# If Organization itself can have fees associated
|
|
144
|
+
has_one :fee, class_name: "::NeetoPaymentsEngine::Fee", as: :feeable, dependent: :destroy
|
|
145
|
+
has_one :split, class_name: "::NeetoPaymentsEngine::Split", dependent: :destroy # If Org defines platform split %
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
# app/models/user.rb (Example if User is a holdable for Stripe Standard)
|
|
149
|
+
class User < ApplicationRecord
|
|
150
|
+
has_one :stripe_connected_account, -> { where(payment_provider: :stripe_standard) }, class_name: "::NeetoPaymentsEngine::Stripe::Account", as: :holdable, dependent: :destroy
|
|
151
|
+
# Add other associations if User can hold other provider accounts
|
|
152
|
+
end
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
Add associations to your **Payable** models (e.g., `Invoice`, `Booking`, `Meeting` - the item being paid for) - you don't need to add this until you create your `Payable` models in your host app:
|
|
156
|
+
```ruby
|
|
157
|
+
# app/models/invoice.rb (Example Payable Model)
|
|
158
|
+
class Invoice < ApplicationRecord
|
|
159
|
+
# Association to the payment record for this invoice
|
|
160
|
+
has_one :payment, class_name: "::NeetoPaymentsEngine::Payment", as: :payable, dependent: :destroy
|
|
161
|
+
|
|
162
|
+
# Optional: If fees are directly configured on the payable item itself
|
|
163
|
+
has_one :fee, class_name: "::NeetoPaymentsEngine::Fee", as: :feeable, dependent: :destroy
|
|
164
|
+
end
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### 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
|
+
|
|
179
|
+
# --- Stripe ---
|
|
180
|
+
# Base URL for OAuth callback (using tunnelto 'connect' subdomain for dev)
|
|
181
|
+
STRIPE_CALLBACK_BASE_URL="https://connect.tunnelto.dev" # Or your tunnel URL
|
|
182
|
+
|
|
183
|
+
# --- Razorpay (if used) ---
|
|
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
|
|
29
188
|
```
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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
|
|
33
222
|
```
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
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!)
|
|
37
260
|
```
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
10. Add the following association to the payable model.
|
|
101
|
-
```ruby
|
|
102
|
-
has_one :payment, as: :payable, class_name: "::NeetoPaymentsEngine::Stripe::Transaction", dependent: :destroy
|
|
261
|
+
|
|
262
|
+
**Secrets Management:**
|
|
263
|
+
- **Development:** Use `.env.local` (which is typically gitignored) to store sensitive keys locally.
|
|
264
|
+
- **Staging/Production:** Use environment variables managed by your deployment platform (e.g., NeetoDeploy, Heroku Config Vars) or Rails encrypted credentials. **Do not commit secrets directly into your repository.**
|
|
265
|
+
|
|
266
|
+
### 4. Stripe Connect Signup
|
|
267
|
+
You **must** sign up for Stripe Connect via the Stripe dashboard, even if you only intend to use a Stripe Platform account. This registration enables the necessary APIs for account management and OAuth flows used by the engine.
|
|
268
|
+
|
|
269
|
+
### 5. OAuth Callback URI Registration
|
|
270
|
+
Register the following callback URIs in your respective payment provider dashboards:
|
|
271
|
+
* **Stripe:** `https://<your_connect_subdomain_or_app_domain>/payments/api/v1/public/stripe/oauth/callback`
|
|
272
|
+
* `<your_connect_subdomain_or_app_domain>`: This should be the publicly accessible URL that routes to your Rails app. In development, this is typically your `tunnelto` URL using the `connect` subdomain (e.g., `https://connect.tunnelto.dev`). In production, it might be your main application domain or a dedicated subdomain.
|
|
273
|
+
* **Razorpay:** `https://<your_razorpay_oauth_base_url>/payments/api/v1/public/razorpay/oauth/callback`
|
|
274
|
+
* `<your_razorpay_oauth_base_url>`: This must match the value you configured for `RAZORPAY_OAUTH_CALLBACK_BASE_URL`. Use the `connect` subdomain via `tunnelto` in development.
|
|
275
|
+
|
|
276
|
+
## Frontend Integration
|
|
277
|
+
|
|
278
|
+
Integrate the React components provided by the `@bigbinary/neeto-payments-frontend` package.
|
|
279
|
+
|
|
280
|
+
### 1. Install Frontend Package
|
|
281
|
+
```bash
|
|
282
|
+
yarn add @bigbinary/neeto-payments-frontend
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
### 2. Install Peer Dependencies
|
|
286
|
+
If the host app already includes all of the following peer deps, then you don't have to install anything explicitly. Use the latest version of each of these peer dependencies if you need to install:
|
|
287
|
+
```bash
|
|
288
|
+
# DO NOT INSTALL THE VERSIONS MENTIONED BELOW AS IT MIGHT BE OUTDATED.
|
|
289
|
+
# ALWAYS PREFER INSTALLING THE LATEST VERSIONS.
|
|
290
|
+
# If something isn't working, then refer to these versions and see
|
|
291
|
+
# what's causing the breakage.
|
|
292
|
+
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
|
+
```
|
|
294
|
+
*Note: Carefully manage potential version conflicts with your host application.*
|
|
295
|
+
|
|
296
|
+
### 3. Components
|
|
297
|
+
Import components into your React application as needed.
|
|
298
|
+
|
|
299
|
+
1. `TaxesDashboard` ([source code](https://github.com/bigbinary/neeto-payments-nano/blob/main/app/javascript/src/components/TaxesDashboard/index.jsx))
|
|
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
|
+
};
|
|
103
323
|
```
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
324
|
+
|
|
325
|
+
2. `PaymentsDashboard` ([source code](https://github.com/bigbinary/neeto-payments-nano/blob/main/app/javascript/src/components/PaymentsDashboard/index.jsx))
|
|
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
|
+
},
|
|
358
|
+
|
|
359
|
+
return (
|
|
360
|
+
<PaymentsDashboard
|
|
361
|
+
{...{ payableEntityColumns, holdableIds }}
|
|
362
|
+
searchProps={SEARCH_PROPS}
|
|
363
|
+
/>
|
|
364
|
+
);
|
|
365
|
+
};
|
|
108
366
|
```
|
|
367
|
+
3. `RefundsDashboard` ([source code](https://github.com/bigbinary/neeto-payments-nano/blob/main/app/javascript/src/components/RefundsDashboard/index.jsx))
|
|
109
368
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
2. [Razorpay integration](/docs/engine/razorpay_integrations.md)
|
|
115
|
-
3. [Stripe payment](/docs/engine/stripe_payment.md)
|
|
116
|
-
4. [Razorpay payment](/docs/engine/razorpay_payment.md)
|
|
117
|
-
|
|
118
|
-
## Frontend package
|
|
119
|
-
### Installation
|
|
120
|
-
1. Install the latest `neeto-payments-frontend` package using the below command:
|
|
121
|
-
```zsh
|
|
122
|
-
yarn add @bigbinary/neeto-payments-frontend
|
|
123
|
-
```
|
|
124
|
-
2. `neeto-payments-frontend` has a few peer dependencies that are required for the proper functioning of the package. Install all the peer dependencies using the below command:
|
|
125
|
-
```zsh
|
|
126
|
-
yarn add @bigbinary/neeto-commons-frontend@2.0.54 @bigbinary/neeto-filters-frontend@2.8.1 @bigbinary/neeto-icons@1.9.22 @bigbinary/neeto-molecules@1.0.9 @bigbinary/neetoui@4.4.10 @honeybadger-io/js@5.1.1 @honeybadger-io/react@5.1.3 axios@1.3.4 classnames@2.3.2 formik@2.2.9 js-logger@1.6.1 mixpanel-browser@2.45.0 ramda@0.28.0 react-helmet@6.1.0 react-query@3.39.3 react-router-dom@5.3.4 react-toastify@8.2.0 yup@1.0.2
|
|
127
|
-
```
|
|
128
|
-
|
|
129
|
-
### Instructions for development
|
|
130
|
-
Check the [Frontend package development guide](https://neeto-engineering.neetokb.com/p/a-d34cb4b0) for step-by-step instructions to develop the frontend package.
|
|
131
|
-
|
|
132
|
-
### Components
|
|
133
|
-
#### 1. `Dashboard` ([source code](https://github.com/bigbinary/neeto-payments-nano/blob/42ad8330442696b102d8555930162d75cf94931e/app/javascript/src/components/Dashboard/index.jsx))
|
|
134
|
-
|
|
135
|
-
**Props**
|
|
136
|
-
- `holdableId`: To specify the ID of the entity that holds the stripe
|
|
137
|
-
account. This is an optional prop. If the value is not specified, the current
|
|
138
|
-
organization ID is taken as the default value while querying for
|
|
139
|
-
transactions.
|
|
140
|
-
- `payableEntityColumns`: To specify the columns from the payable entity
|
|
141
|
-
which need to be additionally displayed. It is an optional prop that defaults
|
|
142
|
-
to `[]` if not specified.
|
|
143
|
-
- `tabs`: To specify the tabs to be displayed in the payments dashboard. It
|
|
144
|
-
is an optional parameter.
|
|
145
|
-
- `headerProps`: To specify the props to be passed to the neetoUI `Header`
|
|
146
|
-
component used in the `Dashboard`.
|
|
147
|
-
|
|
148
|
-
**Configuration**
|
|
149
|
-
- Refer to the [Dashboard](/docs/frontend/dashboard.md) section for detailed information on the available configurations for the `Dashboard` component.
|
|
150
|
-
|
|
151
|
-
**Usage**
|
|
152
|
-
```jsx
|
|
153
|
-
import React, { useState } from "react";
|
|
154
|
-
import { Dashboard } from "@bigbinary/neeto-payments-frontend";
|
|
155
|
-
|
|
156
|
-
const App = () => {
|
|
157
|
-
const headerProps = {
|
|
158
|
-
className: "w-80",
|
|
159
|
-
// Your header properties
|
|
160
|
-
};
|
|
369
|
+
**Props**
|
|
370
|
+
- `payableEntityColumns`: To specify the columns from the payable entity
|
|
371
|
+
which need to be additionally displayed. It is an optional prop that defaults to `[]` if not specified.
|
|
372
|
+
- `searchProps`: Allows specifying additional columns to be included in the search functionality. By default, only the `identifier` column from the refunds table is searchable.
|
|
161
373
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
374
|
+
**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
|
+
|
|
405
|
+
4. `SplitTransfersDashboard` ([source code](https://github.com/bigbinary/neeto-payments-nano/blob/main/app/javascript/src/components/SplitTransfersDashboard/index.jsx))
|
|
406
|
+
|
|
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
|
+
**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
|
+
|
|
433
|
+
return (
|
|
434
|
+
<RefundsDashboard
|
|
435
|
+
{...{ payableEntityColumns }}
|
|
436
|
+
searchProps={SEARCH_PROPS}
|
|
437
|
+
/>
|
|
438
|
+
);
|
|
439
|
+
};
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
|
|
443
|
+
5. `AccountsDashboard` ([source code](https://github.com/bigbinary/neeto-payments-nano/blob/main/app/javascript/src/components/AccountsDashboard/index.jsx))
|
|
444
|
+
|
|
445
|
+
**Usage**
|
|
446
|
+
```jsx
|
|
447
|
+
import React from "react";
|
|
448
|
+
import { AccountsDashboard } from "@bigbinary/neeto-payments-frontend";
|
|
449
|
+
|
|
450
|
+
const App = () => {
|
|
451
|
+
|
|
452
|
+
return (<AccountsDashboard />);
|
|
453
|
+
};
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
6. `PayoutsDashboard` ([source code](https://github.com/bigbinary/neeto-payments-nano/blob/main/app/javascript/src/components/PayoutsDashboard/index.jsx))
|
|
457
|
+
|
|
458
|
+
**Props**
|
|
459
|
+
- `isPlatformEnabled`: Indicates whether platform integration is enabled.
|
|
460
|
+
- `payoutsPageRoute`: The route used to display detailed payout information.
|
|
461
|
+
|
|
462
|
+
|
|
463
|
+
**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
|
+
|
|
513
|
+
**Props**
|
|
514
|
+
- `vpas`: A list of UPI IDs presented for the user to select during confirmation.
|
|
515
|
+
- `identifier`: The unique identifier associated with the payment.
|
|
516
|
+
- `paymentId`: The ID of the payment.
|
|
517
|
+
- `onSuccess`: A callback function triggered upon successful confirmation.
|
|
169
518
|
|
|
170
|
-
|
|
519
|
+
**Usage**
|
|
520
|
+
|
|
521
|
+
```jsx
|
|
522
|
+
import React from "react";
|
|
523
|
+
import { ConfirmUpiPaymentButton } from "@bigbinary/neeto-payments-frontend";
|
|
524
|
+
|
|
525
|
+
const App = () => {
|
|
526
|
+
|
|
527
|
+
return (
|
|
528
|
+
<ConfirmUpiPaymentButton
|
|
529
|
+
identifier={payment.identifier}
|
|
530
|
+
payableId={payment.payableId}
|
|
531
|
+
paymentId={payment.paymentId}
|
|
532
|
+
vpas={meeting.fee.vpas}
|
|
533
|
+
/>
|
|
534
|
+
);
|
|
535
|
+
};
|
|
536
|
+
```
|
|
537
|
+
|
|
538
|
+
### 4. Hooks
|
|
539
|
+
|
|
540
|
+
1. `useStripePromise` ([source code](https://github.com/bigbinary/neeto-payments-nano/blob/main/app/javascript/src/hooks/useStripePromise.js))
|
|
541
|
+
|
|
542
|
+
This hook can used to provide the value for the `stripe` prop of the Stripe `Element` component.
|
|
543
|
+
|
|
544
|
+
**Usage**
|
|
545
|
+
|
|
546
|
+
```jsx
|
|
547
|
+
import React from "react";
|
|
548
|
+
import { useStripePromise } from "@bigbinary/neeto-payments-frontend";
|
|
549
|
+
|
|
550
|
+
const App = () => {
|
|
551
|
+
|
|
552
|
+
const stripePromise = useStripePromise({
|
|
553
|
+
stripePlatformAccount // The integration object for the Stripe platform account. No value is required if the platform account integration is not configured.
|
|
554
|
+
|
|
555
|
+
stripeAccountIdentifier: paymentRecipient?.stripeConnectedAccount?.identifier, // Stripe Standard account integration identifier
|
|
556
|
+
});
|
|
557
|
+
|
|
558
|
+
return (
|
|
559
|
+
<Elements stripe={stripePromise}>
|
|
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))
|
|
565
|
+
|
|
566
|
+
This hook returns a function that can be used to initiate a Razorpay payment. Use it only if you want to trigger the payment flow through a custom button in the host application.
|
|
567
|
+
|
|
568
|
+
|
|
569
|
+
**Usage**
|
|
570
|
+
|
|
571
|
+
```jsx
|
|
572
|
+
import React from "react";
|
|
573
|
+
import { useRazorpayPayment } from "@bigbinary/neeto-payments-frontend";
|
|
574
|
+
|
|
575
|
+
const App = () => {
|
|
576
|
+
|
|
577
|
+
const { makePayment } = useRazorpayPayment({
|
|
578
|
+
payableId, // The ID of the payable entity.
|
|
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.
|
|
583
|
+
|
|
584
|
+
});
|
|
585
|
+
|
|
586
|
+
};
|
|
587
|
+
```
|
|
588
|
+
|
|
589
|
+
### 5. constants
|
|
590
|
+
|
|
591
|
+
1. CURRENCY_OPTIONS
|
|
592
|
+
|
|
593
|
+
A list of supported currencies in `{ value, label }` format. This can be used as options for the NeetoUI `Select` component.
|
|
594
|
+
|
|
595
|
+
### 6. Others
|
|
171
596
|
```
|
|
597
|
+
// Example: Payment Settings Page
|
|
598
|
+
import React, { useState, useEffect } from 'react';
|
|
599
|
+
import axios from 'axios';
|
|
600
|
+
import { PaymentsDashboard, StripeConnect } from '@bigbinary/neeto-payments-frontend';
|
|
601
|
+
import { useQueryParams } from "@bigbinary/neeto-commons-frontend"; // Assuming you use this hook
|
|
602
|
+
|
|
603
|
+
const PaymentSettingsPage = () => {
|
|
604
|
+
const [integrations, setIntegrations] = useState({});
|
|
605
|
+
const [isLoading, setIsLoading] = useState(true);
|
|
606
|
+
const [isStripeConnectOpen, setIsStripeConnectOpen] = useState(false);
|
|
607
|
+
const { app: returnedApp, error: oauthError } = useQueryParams(); // For handling OAuth callbacks
|
|
608
|
+
|
|
609
|
+
useEffect(() => {
|
|
610
|
+
const fetchIntegrations = async () => {
|
|
611
|
+
setIsLoading(true);
|
|
612
|
+
try {
|
|
613
|
+
// IMPORTANT: Specify ONLY the providers your application actually uses.
|
|
614
|
+
const usedProviders = ["stripe_standard"]; // Example: only using Stripe Standard
|
|
615
|
+
const response = await axios.get('/payments/api/v1/integrations', {
|
|
616
|
+
params: { providers: usedProviders }
|
|
617
|
+
});
|
|
618
|
+
setIntegrations(response.data);
|
|
619
|
+
} catch (err) {
|
|
620
|
+
console.error("Failed to fetch integrations:", err);
|
|
621
|
+
// Handle error appropriately (e.g., show toast notification)
|
|
622
|
+
} finally {
|
|
623
|
+
setIsLoading(false);
|
|
624
|
+
}
|
|
625
|
+
};
|
|
626
|
+
fetchIntegrations();
|
|
172
627
|
|
|
173
|
-
|
|
628
|
+
// Handle OAuth callback results
|
|
629
|
+
if (returnedApp === 'stripe' && oauthError) {
|
|
630
|
+
console.error("Stripe connection failed:", oauthError);
|
|
631
|
+
// Show error message to user
|
|
632
|
+
} else if (returnedApp === 'stripe') {
|
|
633
|
+
console.log("Stripe connection initiated successfully. Waiting for webhook verification.");
|
|
634
|
+
// Maybe show a success message, fetchIntegrations will update the state eventually
|
|
635
|
+
}
|
|
636
|
+
// Add similar checks for Razorpay if using OAuth
|
|
174
637
|
|
|
175
|
-
|
|
176
|
-
- `isOpen`: To specify whether the Stripe connect restriction alert should be
|
|
177
|
-
opened or closed.
|
|
178
|
-
- `paymentKind`: To specify the type of Stripe payment kind for rendering
|
|
179
|
-
related content to alert either as "standard" or "platform". If Stripe
|
|
180
|
-
Standard is connected the alert shall forbid Stripe Platform connect and vice
|
|
181
|
-
versa.
|
|
182
|
-
- `onClose`: Handler function that is triggered when the disconnect button is
|
|
183
|
-
clicked.
|
|
638
|
+
}, [returnedApp, oauthError]);
|
|
184
639
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
import { PaymentKindRestrictionAlert } from "@bigbinary/neeto-payments-frontend";
|
|
640
|
+
// Determine holdableId based on your app's logic (e.g., current organization)
|
|
641
|
+
// This might come from context, props, or another state hook
|
|
642
|
+
const currentHoldableId = integrations.stripe_standard_account?.holdable_id || 'your_org_or_user_id'; // Replace with actual logic
|
|
189
643
|
|
|
190
|
-
|
|
191
|
-
|
|
644
|
+
if (isLoading) {
|
|
645
|
+
return <div>Loading payment settings...</div>;
|
|
646
|
+
}
|
|
192
647
|
|
|
193
648
|
return (
|
|
194
|
-
<
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
649
|
+
<div>
|
|
650
|
+
<h1>Payment Settings</h1>
|
|
651
|
+
|
|
652
|
+
{/* Conditionally render based on integration status */}
|
|
653
|
+
{!integrations.stripe_standard_account?.is_connected && (
|
|
654
|
+
<button onClick={() => setIsStripeConnectOpen(true)}>
|
|
655
|
+
Connect Stripe Account
|
|
656
|
+
</button>
|
|
657
|
+
)}
|
|
658
|
+
{integrations.stripe_standard_account?.is_connected && (
|
|
659
|
+
<p>Stripe Account Connected: {integrations.stripe_standard_account.identifier}</p>
|
|
660
|
+
// Add disconnect button here
|
|
661
|
+
)}
|
|
662
|
+
|
|
663
|
+
{/* Stripe Connect Modal */}
|
|
664
|
+
<StripeConnect
|
|
665
|
+
isOpen={isStripeConnectOpen}
|
|
666
|
+
onClose={() => setIsStripeConnectOpen(false)}
|
|
667
|
+
holdableId={currentHoldableId} // Pass the ID of the Organization/User model instance
|
|
668
|
+
returnUrl={`${window.location.origin}/payment-settings`} // URL Stripe redirects back to (should match registered URI)
|
|
669
|
+
isPlatform={false} // Set true only if connecting a Stripe Platform account
|
|
670
|
+
/>
|
|
671
|
+
|
|
672
|
+
{/* Example Dashboard (only show if integrated) */}
|
|
673
|
+
{integrations.stripe_standard_account?.is_connected && (
|
|
674
|
+
<>
|
|
675
|
+
<h2>Payments Overview</h2>
|
|
676
|
+
<PaymentsDashboard
|
|
677
|
+
// Example: Use 'connected' kind if viewing standard account payments
|
|
678
|
+
dashboardKind="connected"
|
|
679
|
+
// Pass holdable ID for the specific account being viewed
|
|
680
|
+
holdableIds={{ stripe_standard: currentHoldableId }}
|
|
681
|
+
/>
|
|
682
|
+
</>
|
|
683
|
+
)}
|
|
684
|
+
</div>
|
|
199
685
|
);
|
|
200
686
|
};
|
|
201
687
|
|
|
202
|
-
export default
|
|
688
|
+
export default PaymentSettingsPage;
|
|
203
689
|
```
|
|
204
690
|
|
|
205
|
-
|
|
691
|
+
### 4. API Calls - Specify Providers
|
|
692
|
+
When calling the `/api/v1/integrations` endpoint (or other APIs that might implicitly check integrations), **you must pass the `providers` parameter** listing only the providers your application uses. Omitting this or including unused providers can lead to errors if the engine tries to load configurations or associations that don't exist for your setup.
|
|
206
693
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
694
|
+
```javascript
|
|
695
|
+
// Correct: Fetching only Stripe Standard integration status
|
|
696
|
+
axios.get('/payments/api/v1/integrations', {
|
|
697
|
+
params: { providers: ["stripe_standard"] }
|
|
698
|
+
});
|
|
210
699
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
import { PayoutsDashboard } from "@bigbinary/neeto-payments-frontend";
|
|
700
|
+
// Incorrect (if not using all providers): Might cause errors
|
|
701
|
+
// axios.get('/payments/api/v1/integrations');
|
|
702
|
+
```
|
|
215
703
|
|
|
216
|
-
|
|
217
|
-
return (
|
|
218
|
-
<PayoutsDashboard
|
|
219
|
-
isPlatformEnabled
|
|
220
|
-
payoutsPageRoute={routes.admin.payment.stripePayouts.show}
|
|
221
|
-
/>
|
|
222
|
-
);
|
|
223
|
-
};
|
|
704
|
+
## Core Concepts
|
|
224
705
|
|
|
225
|
-
|
|
706
|
+
- **Polymorphic Associations:** `payable` (what's being paid for), `accountable` (the payment account, e.g., `Stripe::Account`), and `holdable` (owner of the integration, e.g., `Organization`) link the engine to host app models dynamically based on configuration.
|
|
707
|
+
- **Single Table Inheritance (STI):** Used for provider-specific models like `Payments::Stripe`, `Payments::Razorpay`, `Integrations::Razorpay`.
|
|
708
|
+
- **Service Objects:** Encapsulate business logic (e.g., `Stripe::Payments::CreateService`).
|
|
709
|
+
- **Callbacks:** Host applications implement methods in `NeetoPaymentsEngine::Callbacks` to customize behavior. See the [Callbacks](#callbacks) section.
|
|
710
|
+
- **Webhooks:** Asynchronous events from providers update payment statuses. See [Webhook Handling](#webhook-handling).
|
|
711
|
+
- **JWT Authentication:** Secures OAuth callback flows using RSA keys.
|
|
712
|
+
|
|
713
|
+
## Webhook Handling
|
|
714
|
+
|
|
715
|
+
- Webhooks are crucial for updating payment statuses asynchronously.
|
|
716
|
+
- Endpoints are available at `/payments/api/v1/public/<provider>/webhooks`.
|
|
717
|
+
- Signatures are verified using configured secrets.
|
|
718
|
+
|
|
719
|
+
### Development Environment Setup
|
|
720
|
+
|
|
721
|
+
#### 1. Tunneling
|
|
722
|
+
Use a tool like `ngrok` or `tunnelto` to expose your local development server to the internet. The `connect` subdomain is reserved within Neeto for receiving callbacks from third-party services. Refer to [Using Tunnelto](https://neeto-engineering.neetokb.com/articles/using-tunnelto) and [Exposing Tunnelto Locally](https://neeto-engineering.neetokb.com/articles/writing-payment-tests) for more details.
|
|
723
|
+
```bash
|
|
724
|
+
# Example using tunnelto
|
|
725
|
+
tunnelto --subdomain connect --port <YOUR_RAILS_APP_PORT>
|
|
726
|
+
# This makes https://connect.tunnelto.dev forward to your local app
|
|
226
727
|
```
|
|
227
728
|
|
|
228
|
-
####
|
|
729
|
+
#### 2. Stripe Webhook Setup (Manual)
|
|
730
|
+
* Go to your **Stripe Test Dashboard** > Developers > Webhooks.
|
|
731
|
+
* Click "Add endpoint".
|
|
732
|
+
* Endpoint URL: `https://connect.tunnelto.dev/payments/api/v1/public/stripe/webhooks` (replace with your actual tunnel URL).
|
|
733
|
+
* Select events to listen to. Refer to `config/stripe/webhooks.yml` in the engine for the required list (e.g., `payment_intent.succeeded`, `charge.refunded`, `account.updated`).
|
|
734
|
+
* Click "Add endpoint".
|
|
735
|
+
* After creation, find the **Signing secret** (starts with `whsec_...`).
|
|
736
|
+
* **Copy this secret** and set it as the `STRIPE_WEBHOOK_SECRET` in your local `.env.local` or development credentials.
|
|
737
|
+
* **Note:** The `neeto_payments_engine:stripe:webhooks:subscribe` rake task is **not recommended** for development setup as it doesn't create the endpoint and might show outdated secrets. Manual setup provides better control and ensures you have the correct, current secret. Refer to the [Official Stripe Webhook Documentation](https://docs.stripe.com/webhooks) for more details.
|
|
229
738
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
739
|
+
#### 3. Razorpay Webhook Setup (Manual)
|
|
740
|
+
* Go to your **Razorpay Partners Dashboard**.
|
|
741
|
+
* Go to Applications
|
|
742
|
+
* Create a new application or open the existing application
|
|
743
|
+
* Add test webhook
|
|
744
|
+
* Webhook URL: `https://connect.tunnelto.dev/payments/api/v1/public/razorpay/webhooks` (replace with your tunnel URL).
|
|
745
|
+
* Set a **Secret** (create a strong random string).
|
|
746
|
+
* Select Active Events (e.g., `payment.authorized`, `payment.captured`, `payment.failed`, `refund.processed`, `refund.failed`).
|
|
747
|
+
* Save the webhook.
|
|
748
|
+
* **Copy the Secret you set** and configure it as `RAZORPAY_WEBHOOK_SECRET` in your local development environment.
|
|
238
749
|
|
|
239
|
-
|
|
240
|
-
```jsx
|
|
241
|
-
import React from "react";
|
|
242
|
-
import { TaxesDashboard } from "@bigbinary/neeto-payments-frontend";
|
|
750
|
+
## Authentication (JWT for OAuth)
|
|
243
751
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
breadcrumbs={[{text: "Settings" ,link: routes.admin.form.settings}]}
|
|
249
|
-
/>
|
|
250
|
-
);
|
|
251
|
-
};
|
|
752
|
+
- Secures the OAuth callback flow for connecting Stripe/Razorpay accounts.
|
|
753
|
+
- Uses RSA public/private keys (`CONNECT_PUBLIC_KEY`, `CONNECT_PRIVATE_KEY`) configured in your secrets.
|
|
754
|
+
- The `ConnectLinkService` creates a short-lived JWT containing user context when initiating the OAuth flow.
|
|
755
|
+
- The `JwtService` verifies the token signature using the public key upon callback.
|
|
252
756
|
|
|
253
|
-
|
|
757
|
+
## Callbacks
|
|
758
|
+
|
|
759
|
+
Host applications **must** implement specific methods within a `NeetoPaymentsEngine::Callbacks` module (e.g., in `app/lib/neeto_payments_engine/callbacks.rb`) to tailor the engine's behavior to their domain logic.
|
|
760
|
+
|
|
761
|
+
### Mandatory Callbacks
|
|
762
|
+
```ruby
|
|
763
|
+
# app/lib/neeto_payments_engine/callbacks.rb
|
|
764
|
+
module NeetoPaymentsEngine
|
|
765
|
+
class Callbacks
|
|
766
|
+
# --- Mandatory Callbacks ---
|
|
767
|
+
|
|
768
|
+
# Check user permissions for general payment management access.
|
|
769
|
+
# @param user [User] The user attempting the action.
|
|
770
|
+
# @return [Boolean] True if the user has permission.
|
|
771
|
+
def self.can_manage_payments?(user)
|
|
772
|
+
user.has_role?(:admin) # Replace with your authorization logic
|
|
773
|
+
end
|
|
774
|
+
|
|
775
|
+
# ... other callbacks ...
|
|
776
|
+
end
|
|
777
|
+
end
|
|
254
778
|
```
|
|
255
779
|
|
|
256
|
-
###
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
780
|
+
### Optional Callbacks (Implement as needed)
|
|
781
|
+
```ruby
|
|
782
|
+
# app/lib/neeto_payments_engine/callbacks.rb
|
|
783
|
+
module NeetoPaymentsEngine
|
|
784
|
+
class Callbacks
|
|
785
|
+
# --- Optional Callbacks (Implement as needed) ---
|
|
786
|
+
|
|
787
|
+
# Return the fee object applicable to the payable item.
|
|
788
|
+
# @param payable [Object] The host application's payable model instance (e.g., Invoice, Booking).
|
|
789
|
+
# @return [NeetoPaymentsEngine::Fee, nil] The Fee object or nil if no fee applies.
|
|
790
|
+
def self.get_fee(payable)
|
|
791
|
+
# Example: Find fee based on payable's attributes or associated product/service
|
|
792
|
+
payable.applicable_fee # Replace with your actual logic
|
|
793
|
+
end
|
|
794
|
+
|
|
795
|
+
# Check if a payment can be processed for this payable item.
|
|
796
|
+
# @param payable [Object] The host application's payable model instance.
|
|
797
|
+
# @return [Boolean] True if payment is allowed, false otherwise.
|
|
798
|
+
def self.payment_processable?(payable)
|
|
799
|
+
# Example: Check if the payable is in a state allowing payment (e.g., 'pending', 'unpaid')
|
|
800
|
+
payable.can_be_paid? # Replace with your actual logic
|
|
801
|
+
end
|
|
802
|
+
|
|
803
|
+
# Return an ActiveRecord::Relation scope for payable objects.
|
|
804
|
+
# Used by the engine, e.g., when filtering payments by associated payables.
|
|
805
|
+
# @param organization [Organization] The organization context.
|
|
806
|
+
# @param payable_type [String, nil] The stringified class name of the payable type (e.g., "Invoice"), or nil.
|
|
807
|
+
# @return [ActiveRecord::Relation] A scope of payable objects.
|
|
808
|
+
def self.get_payables(organization, payable_type = nil)
|
|
809
|
+
# Example: Return all Invoices for the organization if type matches
|
|
810
|
+
if payable_type == "Invoice"
|
|
811
|
+
organization.invoices
|
|
812
|
+
elsif payable_type == "Booking"
|
|
813
|
+
organization.bookings
|
|
814
|
+
else
|
|
815
|
+
# Return a sensible default or handle all relevant types
|
|
816
|
+
organization.invoices # Defaulting to invoices here, adjust as needed
|
|
817
|
+
end
|
|
818
|
+
end
|
|
819
|
+
|
|
820
|
+
# Validate if a discount code is applicable to a specific payable item.
|
|
821
|
+
# @param discount_code [DiscountCode] Host application's discount code model instance.
|
|
822
|
+
# @param payable [Object] The host application's payable model instance.
|
|
823
|
+
# @return [Boolean] True if the discount code is valid for the payable, false otherwise.
|
|
824
|
+
def self.valid_discount_code?(discount_code, payable)
|
|
825
|
+
# Example: Check code validity, usage limits, applicability to the payable's item/service
|
|
826
|
+
discount_code.is_valid_for?(payable) # Replace with your actual logic
|
|
827
|
+
end
|
|
828
|
+
|
|
829
|
+
# Determine if the transaction cookie (used by Stripe flow) should be deleted.
|
|
830
|
+
# Default logic (return true) is usually sufficient. Delete after success/refund.
|
|
831
|
+
# @param payment [NeetoPaymentsEngine::Payment] The payment object.
|
|
832
|
+
# @return [Boolean] True to delete the cookie, false to keep it.
|
|
833
|
+
def self.delete_transaction_cookie?(payment)
|
|
834
|
+
true
|
|
835
|
+
end
|
|
836
|
+
|
|
837
|
+
# Return the Stripe connected account associated with the payable's context.
|
|
838
|
+
# Crucial for marketplace payments where funds go to a connected account.
|
|
839
|
+
# @param payable [Object] The host application's payable model instance.
|
|
840
|
+
# @return [NeetoPaymentsEngine::Stripe::Account, nil] The connected account or nil.
|
|
841
|
+
def self.get_connected_account(payable)
|
|
842
|
+
# Example: Find the seller/vendor associated with the payable item
|
|
843
|
+
payable.seller&.stripe_connected_account # Replace with your actual logic
|
|
844
|
+
end
|
|
845
|
+
|
|
846
|
+
# Return the Stripe connected account that should receive the split amount for a given payable.
|
|
847
|
+
# Often the same logic as get_connected_account.
|
|
848
|
+
# @param payable [Object] The host application's payable model instance.
|
|
849
|
+
# @return [NeetoPaymentsEngine::Stripe::Account, nil] The destination account for the split.
|
|
850
|
+
def self.get_split_account(payable)
|
|
851
|
+
# Example: Find the recipient/beneficiary for the split payment
|
|
852
|
+
payable.recipient_user&.stripe_connected_account # Replace with your actual logic
|
|
853
|
+
end
|
|
854
|
+
|
|
855
|
+
# Return a hash of details for a payable item, used in CSV exports.
|
|
856
|
+
# @param payable [Object] The host application's payable model instance.
|
|
857
|
+
# @return [Hash] Key-value pairs to include in the export.
|
|
858
|
+
def self.get_exportable_details(payable)
|
|
859
|
+
{
|
|
860
|
+
# Example: Extract relevant fields from your payable model
|
|
861
|
+
payable_name: payable.try(:name) || "N/A",
|
|
862
|
+
invoice_number: payable.try(:invoice_number),
|
|
863
|
+
product_sku: payable.try(:product_sku)
|
|
864
|
+
}
|
|
865
|
+
end
|
|
866
|
+
|
|
867
|
+
# Return the primary custom hostname for an organization.
|
|
868
|
+
# Used for registering Stripe Payment Method Domains.
|
|
869
|
+
# @param organization [Organization] The organization context.
|
|
870
|
+
# @return [String, nil] The custom hostname or nil.
|
|
871
|
+
def self.custom_domain_hostname(organization)
|
|
872
|
+
# Example: Fetch from a dedicated custom domain model or setting
|
|
873
|
+
organization.custom_domain_setting&.hostname
|
|
874
|
+
end
|
|
875
|
+
|
|
876
|
+
# Determine if a payment related to this payable can be split.
|
|
877
|
+
# @param payable [Object] The host application's payable model instance.
|
|
878
|
+
# @return [Boolean] True if splits are allowed for this payable type/context.
|
|
879
|
+
def self.splittable?(payable)
|
|
880
|
+
# Example: Enable splits only for specific product types or services
|
|
881
|
+
payable.allows_splits? # Replace with your actual logic
|
|
882
|
+
end
|
|
883
|
+
|
|
884
|
+
# Determine if a split transfer should be initiated immediately or held.
|
|
885
|
+
# @param payable [Object] The host application's payable model instance.
|
|
886
|
+
# @return [Boolean] True to transfer immediately, false to hold (e.g., for review).
|
|
887
|
+
def self.transferable?(payable)
|
|
888
|
+
# Example: Hold transfers for items pending review or fulfillment
|
|
889
|
+
!payable.needs_manual_review? # Replace with your actual logic
|
|
890
|
+
end
|
|
891
|
+
|
|
892
|
+
# Return metadata about the payable to include in Stripe transfers.
|
|
893
|
+
# @param payable [Object] The host application's payable model instance.
|
|
894
|
+
# @return [Hash] Metadata hash (keys/values must be strings, limited length).
|
|
895
|
+
def self.get_payable_details(payable)
|
|
896
|
+
{
|
|
897
|
+
payable_id: payable.id.to_s,
|
|
898
|
+
payable_type: payable.class.name,
|
|
899
|
+
# Add other relevant identifiers (e.g., order_id, booking_ref)
|
|
900
|
+
order_reference: payable.try(:order_reference).to_s
|
|
901
|
+
}
|
|
902
|
+
end
|
|
903
|
+
|
|
904
|
+
# Check permissions specifically for managing the Stripe Platform account (if applicable).
|
|
905
|
+
# @param user [User] The user attempting the action.
|
|
906
|
+
# @return [Boolean] True if the user has permission.
|
|
907
|
+
def self.can_manage_stripe_platform_account?(user)
|
|
908
|
+
user.has_role?(:super_admin) # Replace with your authorization logic
|
|
909
|
+
end
|
|
910
|
+
|
|
911
|
+
# Check permissions specifically for managing Razorpay integrations.
|
|
912
|
+
# @param user [User] The user attempting the action.
|
|
913
|
+
# @return [Boolean] True if the user has permission.
|
|
914
|
+
def self.can_manage_razorpay_integration?(user)
|
|
915
|
+
user.has_role?(:admin) # Replace with your authorization logic
|
|
916
|
+
end
|
|
917
|
+
end
|
|
918
|
+
end
|
|
275
919
|
```
|
|
276
920
|
|
|
277
|
-
|
|
278
|
-
|
|
921
|
+
## Exposed Entities
|
|
922
|
+
|
|
923
|
+
The engine exposes various models, services, jobs, and tasks for integration and extension:
|
|
924
|
+
|
|
925
|
+
- **Models:** `Payment`, `Fee`, `Refund`, `Split`, `Payments::Split`, `Stripe::Account`, `Stripe::PlatformAccount`, `Integration`, `Customer`, `PaymentMethod`, `Payout`, `WebhookEvent`, etc. Host apps primarily interact with these through ActiveRecord associations defined on their own models.
|
|
926
|
+
- **Services:** Encapsulate core logic (e.g., `Stripe::Payments::CreateService`, `Razorpay::Accounts::CreateService`, `SplitTransfersFilterService`, `ExportCsvService`). While mostly used internally, filter and export services might be invoked directly or indirectly via controllers.
|
|
927
|
+
- **Jobs:** Handle background tasks (`Stripe::WebhooksJob`, `StripePlatform::CreatePaymentSplitsJob`, `ExportCsvJob`, `CreatePaymentMethodDomainJob`). These are queued and processed by Sidekiq.
|
|
928
|
+
- **Concerns:** Reusable modules (`Amountable`, `PaymentProcessable`, `Taxable`, `Stripe::Accountable`, `Refundable`). Mostly for internal engine use.
|
|
929
|
+
- **Rake Tasks:**
|
|
930
|
+
- `neeto_payments_engine:stripe:account:integrate`: Seeds a sample Stripe connected account. **Development/Testing only.**
|
|
931
|
+
- `neeto_payments_engine:stripe:webhooks:subscribe`: **Do not rely on this for setup.** See [Webhook Handling](#webhook-handling) for manual setup instructions.
|
|
932
|
+
|
|
933
|
+
## API Endpoints
|
|
934
|
+
|
|
935
|
+
The engine exposes several API endpoints under the configured mount path (default `/payments`). Here are some key ones:
|
|
936
|
+
|
|
937
|
+
| Method | Path | Description | Authentication |
|
|
938
|
+
| :----- | :------------------------------------------------ | :----------------------------------------------------------------------- | :------------- |
|
|
939
|
+
| GET | `/api/v1/integrations` | List integrated payment provider accounts for the current context. | Host App Auth |
|
|
940
|
+
| POST | `/api/v1/exports` | Initiate a CSV export for dashboards (Payments, Refunds, Splits). | Host App Auth |
|
|
941
|
+
| GET | `/api/v1/payments` | List payments with filtering and pagination. | Host App Auth |
|
|
942
|
+
| GET | `/api/v1/refunds` | List refunds with filtering and pagination. | Host App Auth |
|
|
943
|
+
| PUT | `/api/v1/recurring_payments/:id` | Update the status of an admin-managed recurring payment. | Host App Auth |
|
|
944
|
+
| GET | `/api/v1/split_transfers` | List split transfers (Stripe Platform) with filtering. | Host App Auth |
|
|
945
|
+
| GET | `/api/v1/split_transfers/:id` | Show details of a specific split transfer. | Host App Auth |
|
|
946
|
+
| POST | `/api/v1/split_transfers/bulk_update` | Cancel or resume multiple pending/cancelled split transfers. | Host App Auth |
|
|
947
|
+
| GET | `/api/v1/fees/:fee_id/taxes` | List taxes configured for a specific fee. | Host App Auth |
|
|
948
|
+
| POST | `/api/v1/fees/:fee_id/taxes` | Create a new tax for a fee. | Host App Auth |
|
|
949
|
+
| PUT | `/api/v1/fees/:fee_id/taxes/:id` | Update an existing tax for a fee. | Host App Auth |
|
|
950
|
+
| DELETE | `/api/v1/fees/:fee_id/taxes/:id` | Delete a tax from a fee. | Host App Auth |
|
|
951
|
+
| GET | `/api/v1/fees/:fee_id/recurring_setting` | Get recurring payment settings for a fee. | Host App Auth |
|
|
952
|
+
| PUT | `/api/v1/fees/:fee_id/recurring_setting` | Update recurring payment settings for a fee. | Host App Auth |
|
|
953
|
+
| GET | `/api/v1/stripe/countries` | List countries supported by Stripe for account creation. | Host App Auth |
|
|
954
|
+
| POST | `/api/v1/stripe/accounts` | Initiate Stripe connected account creation/onboarding. | Host App Auth |
|
|
955
|
+
| GET | `/api/v1/stripe/accounts/:id/creation_status` | Check the status of an async account creation job. | Host App Auth |
|
|
956
|
+
| DELETE | `/api/v1/stripe/accounts/:id` | Disconnect a Stripe connected account. | Host App Auth |
|
|
957
|
+
| GET | `/api/v1/stripe_platform/account` | Get details of the configured Stripe Platform account. | Host App Auth |
|
|
958
|
+
| POST | `/api/v1/stripe_platform/account` | Create/Connect the Stripe Platform account. | Host App Auth |
|
|
959
|
+
| PUT | `/api/v1/stripe_platform/account` | Update Stripe Platform account settings. | Host App Auth |
|
|
960
|
+
| DELETE | `/api/v1/stripe_platform/account` | Disconnect the Stripe Platform account. | Host App Auth |
|
|
961
|
+
| GET | `/api/v1/stripe_platform/payouts` | List payouts made from the Stripe Platform account. | Host App Auth |
|
|
962
|
+
| GET | `/api/v1/razorpay/holdables/:holdable_id/account` | Get Razorpay account details for a specific holdable (Deprecated style). | Host App Auth |
|
|
963
|
+
| GET | `/api/v1/upi/vpas` | List configured UPI VPAs for the organization. | Host App Auth |
|
|
964
|
+
| POST | `/api/v1/upi/vpas` | Add a new UPI VPA. | Host App Auth |
|
|
965
|
+
| DELETE | `/api/v1/upi/vpas/:id` | Delete a UPI VPA. | Host App Auth |
|
|
966
|
+
| PUT | `/api/v1/upi/payments/:id` | Confirm/update a manual UPI payment (Admin action). | Host App Auth |
|
|
967
|
+
| GET | `/api/v1/integrations/connect/stripe` | Start Stripe Connect OAuth flow. | Host App Auth |
|
|
968
|
+
| GET | `/api/v1/integrations/connect/razorpay` | Start Razorpay OAuth flow. | Host App Auth |
|
|
969
|
+
| POST | `/api/v1/public/stripe/transactions` | Create a Stripe Payment Intent (Public endpoint). | Open |
|
|
970
|
+
| POST | `/api/v1/public/razorpay/payments` | Create a Razorpay Order (Public endpoint). | Open |
|
|
971
|
+
| POST | `/api/v1/public/upi/payments` | Initiate a manual UPI payment (Public endpoint). | Open |
|
|
972
|
+
| GET | `/api/v1/public/recurring_payments/:payable_id` | Get status of a customer-managed recurring payment. | Open |
|
|
973
|
+
| PUT | `/api/v1/public/recurring_payments/:payable_id` | Allow customer to cancel their recurring payment. | Open |
|
|
974
|
+
| GET | `/api/v1/public/stripe/oauth/authorize` | Stripe OAuth authorization step (Redirect handled by engine). | JWT |
|
|
975
|
+
| GET | `/api/v1/public/stripe/oauth/callback` | Stripe OAuth callback processing endpoint. | JWT |
|
|
976
|
+
| GET | `/api/v1/public/razorpay/oauth/authorize` | Razorpay OAuth authorization step (Redirect handled by engine). | JWT |
|
|
977
|
+
| GET | `/api/v1/public/razorpay/oauth/callback` | Razorpay OAuth callback processing endpoint. | JWT |
|
|
978
|
+
| POST | `/api/v1/public/stripe/webhooks` | Stripe webhook receiver endpoint. | Stripe Sig. |
|
|
979
|
+
| POST | `/api/v1/public/razorpay/webhooks` | Razorpay webhook receiver endpoint. | Razorpay Sig. |
|
|
980
|
+
| POST | `/api/v1/public/stripe_platform/webhooks` | Stripe Platform webhook receiver endpoint. | Stripe Sig. |
|
|
981
|
+
|
|
982
|
+
*Note: "Host App Auth" means the endpoint relies on the host application's authentication (e.g., `authenticate_user!`). "JWT" means authentication relies on the JWT generated during the OAuth flow. "Open" means no authentication. "Stripe Sig." / "Razorpay Sig." means verification is done via webhook signatures.*
|
|
983
|
+
|
|
984
|
+
## Incineration Concern
|
|
985
|
+
|
|
986
|
+
If your host application uses `neeto-org-incineration-engine`, you need to integrate `NeetoPaymentsEngine` models correctly. The `NeetoPaymentsEngine::Fee` model often requires special handling as it might be associated with host application models (`feeable`).
|
|
987
|
+
|
|
988
|
+
1. **Initial Setup:** When first adding `neeto-payments-engine`, add `NeetoPaymentsEngine::Fee` to the `SKIPPED_MODELS` list in your host application's `IncinerableConcern` implementation (e.g., in `app/models/concerns/incinerable_concern.rb`). This prevents incineration errors before associations are correctly set up especially while running the whole test suite in host app.
|
|
989
|
+
```ruby
|
|
990
|
+
# In your host app's IncinerableConcern
|
|
991
|
+
# TODO: Incinerate Fee based on Entity later
|
|
992
|
+
SKIPPED_MODELS = ["NeetoActivitiesEngine::Activity", "NeetoPaymentsEngine::Fee"]
|
|
993
|
+
```
|
|
994
|
+
2. **Define Associations Later(optional):** Add the `has_one :fee, as: :feeable` association to your host application models that should have fees (e.g., `Product`, `ServicePlan`).
|
|
995
|
+
3. **Update Incineration Logic(optional):** Once associations are defined, **remove** `"NeetoPaymentsEngine::Fee"` from `SKIPPED_MODELS`. You must then update your `IncinerableConcern.associated_models` method to include logic that correctly finds and targets `NeetoPaymentsEngine::Fee` records associated with the organization being incinerated, likely through the `feeable` association.
|
|
996
|
+
|
|
997
|
+
```ruby
|
|
998
|
+
# In your host app's IncinerableConcern
|
|
999
|
+
def self.associated_models(org_id)
|
|
1000
|
+
# Add logic for NeetoPaymentsEngine::Fee
|
|
1001
|
+
"NeetoPaymentsEngine::Fee": {
|
|
1002
|
+
# Example: Find fees associated with Products belonging to the org
|
|
1003
|
+
joins: "JOIN products ON products.id = neeto_payments_engine_fees.feeable_id AND neeto_payments_engine_fees.feeable_type = 'Product'",
|
|
1004
|
+
where: ["products.organization_id = ?", org_id]
|
|
1005
|
+
# Adjust join and where clause based on your actual feeable models
|
|
1006
|
+
},
|
|
1007
|
+
# Add logic for other NeetoPaymentsEngine models if needed,
|
|
1008
|
+
# though many are handled via cascading deletes or direct Org association.
|
|
1009
|
+
# Refer to the engine's IncinerableConcern for models it handles itself.
|
|
1010
|
+
# "NeetoPaymentsEngine::Payment": { ... }
|
|
1011
|
+
|
|
1012
|
+
# rest of the code
|
|
1013
|
+
end
|
|
1014
|
+
```
|
|
1015
|
+
Consult the engine's own `NeetoPaymentsEngine::IncinerableConcern` (`app/models/concerns/neeto_payments_engine/incinerable_concern.rb`) for the list of models it defines and how it targets them for deletion, to avoid duplication and ensure comprehensive cleanup.
|
|
279
1016
|
|
|
280
|
-
|
|
1017
|
+
## Deprecated Patterns
|
|
281
1018
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
1019
|
+
Please be aware of the following deprecations and use the recommended alternatives:
|
|
1020
|
+
|
|
1021
|
+
1. **Configuration `holdable_class`:**
|
|
1022
|
+
* **Deprecated:** The singular `NeetoPaymentsEngine.holdable_class = "ClassName"` configuration.
|
|
1023
|
+
* **Recommended:** Use the hash-based `NeetoPaymentsEngine.providers_holdable_class = { provider: "ClassName", ... }` to configure holdable models per provider. This provides more flexibility.
|
|
1024
|
+
|
|
1025
|
+
2. **Holdable-Scoped APIs:**
|
|
1026
|
+
* **Deprecated:** API endpoints previously located under `/api/v1/<provider>/holdables/:holdable_id/...` are deprecated.
|
|
1027
|
+
* **Recommended:** Use the newer API structure:
|
|
1028
|
+
* For general integration status: `/api/v1/integrations` (passing the `providers` param).
|
|
1029
|
+
* For provider-specific account management: `/api/v1/<provider>/accounts/...`.
|
|
1030
|
+
* For provider-specific resources like payouts: `/api/v1/<provider>/payouts/...`.
|
|
1031
|
+
|
|
1032
|
+
3. **`Charge` Model/Concept:**
|
|
1033
|
+
* **Deprecated:** Older versions might have referred to payment processing entities as `Charge`.
|
|
1034
|
+
* **Recommended:** The primary entity for payment processing is now `NeetoPaymentsEngine::Payment` (and its STI subclasses like `Payments::Stripe`). Use `Payment` in associations and logic. The concept of `Fee` (`NeetoPaymentsEngine::Fee`) represents the configuration of *what* to charge, while `Payment` represents the actual transaction.
|
|
1035
|
+
|
|
1036
|
+
## Development Environment Setup
|
|
1037
|
+
|
|
1038
|
+
To run the engine locally integrated with a host application, ensure the following processes are running:
|
|
1039
|
+
|
|
1040
|
+
1. **Rails Server:** Starts the main web application.
|
|
1041
|
+
```bash
|
|
1042
|
+
bundle exec rails s
|
|
1043
|
+
```
|
|
1044
|
+
2. **Vite Dev Server:** Compiles and serves frontend assets.
|
|
1045
|
+
```bash
|
|
1046
|
+
# If using Vite
|
|
1047
|
+
yarn dev
|
|
1048
|
+
```
|
|
1049
|
+
3. **Sidekiq Worker:** Processes background jobs (like webhook handling).
|
|
1050
|
+
```bash
|
|
1051
|
+
bundle exec sidekiq -e development -C config/sidekiq.yml
|
|
1052
|
+
```
|
|
1053
|
+
4. **Tunneling Service (for Webhooks/OAuth):** Exposes your local server to the internet. Use the `connect` subdomain for OAuth callbacks.
|
|
1054
|
+
```bash
|
|
1055
|
+
# Using tunnelto (replace <YOUR_APP_PORT> with your Rails server port, e.g., 3000)
|
|
1056
|
+
tunnelto --subdomain connect --port <YOUR_APP_PORT>
|
|
1057
|
+
```
|
|
1058
|
+
*Note: The `connect` subdomain is conventionally used across Neeto applications for receiving callbacks from third-party services like Stripe/Razorpay OAuth.*
|
|
285
1059
|
|
|
286
|
-
|
|
287
|
-
```
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
1060
|
+
The `Procfile.dev.PAYMENTS` might look like this:
|
|
1061
|
+
```yaml
|
|
1062
|
+
web: bundle exec rails s
|
|
1063
|
+
vite: yarn dev
|
|
1064
|
+
worker: bundle exec sidekiq -e development -C config/sidekiq.yml
|
|
1065
|
+
# Tunnel needs to be run in a separate terminal
|
|
292
1066
|
```
|
|
293
1067
|
|
|
294
|
-
|
|
295
|
-
|
|
1068
|
+
## Helper methods
|
|
1069
|
+
- `currency_format_with_symbol`
|
|
1070
|
+
This helper method converts a currency code (like "INR" or "USD") into its corresponding symbol and returns the formatted amount with the symbol. Example $10.00.
|
|
1071
|
+
|
|
1072
|
+
Usage in host application:
|
|
1073
|
+
- In general modules (e.g., Services or View helpers)
|
|
1074
|
+
```ruby
|
|
1075
|
+
module ApplicationHelper
|
|
1076
|
+
include ::NeetoPaymentsEngine::CurrencyFormatHelper
|
|
1077
|
+
|
|
1078
|
+
def formatted_amount
|
|
1079
|
+
currency_format_with_symbol(payment&.amount, payment&.currency)
|
|
1080
|
+
end
|
|
1081
|
+
end
|
|
1082
|
+
```
|
|
1083
|
+
|
|
1084
|
+
- In mailers
|
|
1085
|
+
|
|
1086
|
+
1. Include the helper in your mailer:
|
|
1087
|
+
```ruby
|
|
1088
|
+
class Packages::PurchaseConfirmedMailer < ApplicationMailer
|
|
1089
|
+
helper ::NeetoPaymentsEngine::CurrencyFormatHelper
|
|
1090
|
+
|
|
1091
|
+
def customer_email
|
|
1092
|
+
end
|
|
1093
|
+
end
|
|
1094
|
+
```
|
|
1095
|
+
2. Use the method in the mailer view:
|
|
1096
|
+
```ruby
|
|
1097
|
+
<%= "#{currency_format_with_symbol(@purchase.payment&.amount,@purchase.payment&.currency)}" %>
|
|
1098
|
+
```
|
|
1099
|
+
|
|
1100
|
+
## Testing & Debugging
|
|
1101
|
+
|
|
1102
|
+
- **Dummy App:** Use the `test/dummy` app within the engine's repository for isolated testing.
|
|
1103
|
+
- **Test Helpers:** Utilize `NeetoPaymentsEngine::TestHelpers` (includes `HttpRequestHelpers`) for stubbing API calls to Stripe/Razorpay (`test/helpers/http_request_helpers/`).
|
|
1104
|
+
- **Stripe Test Cards:**
|
|
1105
|
+
* Valid Card: `4242 4242 4242 4242`
|
|
1106
|
+
* Declined Card: `4000 0000 0000 0002`
|
|
1107
|
+
* Expiry Date: Any future date (e.g., `12/30`)
|
|
1108
|
+
* CVC: Any 3 digits (e.g., `123`)
|
|
1109
|
+
* ZIP Code: Any valid ZIP (e.g., `12345`)
|
|
1110
|
+
* [More Stripe Test Cards](https://docs.stripe.com/testing)
|
|
1111
|
+
- **Razorpay Test Cards/UPI:** Refer to [Razorpay Testing Docs](https://razorpay.com/docs/payments/payments/test-card-details/).
|
|
1112
|
+
- **Logging:** Check Rails logs (`log/development.log`) for detailed output from the engine's `LogActivityHelper`.
|
|
1113
|
+
- **Provider Dashboards:** Use the Stripe and Razorpay **Test Mode** dashboards to view API logs, payment details, webhook attempts, and specific error messages.
|
|
1114
|
+
- **JWT Debugging:** Use tools like [jwt.io](https://jwt.io) to decode JWTs generated during OAuth flows. Paste the token and the **public key** (`CONNECT_PUBLIC_KEY`) to verify the signature and inspect the payload (check `exp` claim for expiry).
|
|
1115
|
+
|
|
1116
|
+
## Gotchas & Tips
|
|
1117
|
+
|
|
1118
|
+
- **`providers_holdable_class` is Mandatory:** Forgetting to configure this in the initializer will lead to errors when the engine tries to find associated accounts.
|
|
1119
|
+
- **Specify `providers` in API Calls:** When calling `/api/v1/integrations`, always pass the `providers` param listing only the providers you actually use in your host app (e.g., `params: { providers: ["stripe_standard"] }`). Failing to do so might cause errors if the engine tries to load an unconfigured provider (like UPI).
|
|
1120
|
+
- **Stripe Connect Signup:** You *must* complete the Stripe Connect signup process in your Stripe account, even for platform-only usage.
|
|
1121
|
+
- **Webhook Secrets in Dev:** Manually created webhook endpoint secrets from Stripe/Razorpay dashboards are the source of truth for development, not necessarily what rake tasks might print.
|
|
1122
|
+
- **JWT Key Security:** Treat your JWT private key with the same security as your API secret keys.
|
|
1123
|
+
- **Migration Order:** Always run `bundle exec rails neeto_payments_engine:install:migrations` *before* `db:migrate` when setting up or upgrading. We also need to run this rake task and migration after we run `./bin/setup` in the host app.
|
|
1124
|
+
|
|
1125
|
+
## Publishing
|
|
1126
|
+
|
|
1127
|
+
For instructions on building and releasing the `@bigbinary/neeto-payments-frontend` NPM package and the `neeto-payments-engine` Ruby gem, please refer to the internal guide: [Building and Releasing Packages](https://neeto-engineering.neetokb.com/articles/building-and-releasing-packages).
|