🚀 Big News: Socket Acquires Coana to Bring Reachability Analysis to Every Appsec Team.Learn more
Socket
Book a DemoInstallSign in
Socket

@bigbinary/neeto-payments-frontend

Package Overview
Dependencies
Maintainers
3
Versions
211
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@bigbinary/neeto-payments-frontend

To manage payments across the neeto products.

3.3.47
latest
npm
Version published
Weekly downloads
622
-3.86%
Maintainers
3
Weekly downloads
 
Created
Source

neeto-payments-nano

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.

This engine enables host applications to:

  • Integrate with multiple payment providers (Stripe, Razorpay, UPI).
  • Process payments.
  • Handle payment splits for marketplace scenarios (Stripe).
  • Manage refunds.
  • Store and reuse payment methods securely (optional).
  • Configure fee structures and apply taxes/discounts.
  • Provide administrative dashboards for monitoring transactions, refunds, payouts, and accounts.

Table of Contents

Installation (Backend Engine)

Follow these steps to integrate the neeto-payments-engine into your host Rails application:

1. Add the Gem

Add the gem to your application's Gemfile:

# Gemfile
source "NEETO_GEM_SERVER_URL" do
  gem "neeto-payments-engine"
end

2. Install Gems

Run bundler to install the gem and its dependencies:

bundle install

3. Install Migrations

Copy the engine's database migrations into your host application. This step is crucial.

bundle exec rails neeto_payments_engine:install:migrations

4. Run Migrations

Apply the migrations to your database:

bundle exec rails db:migrate

5. Mount the Engine

Add the engine's routes to your application's config/routes.rb:

# config/routes.rb
mount NeetoPaymentsEngine::Engine, at: "/payments" # Or your preferred mount point

This makes the engine's API endpoints available, by default under /payments.

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.

Configuration (Backend Engine)

1. Initializer

Create an initializer file config/initializers/neeto_payments_engine.rb to configure the engine:

# config/initializers/neeto_payments_engine.rb
NeetoPaymentsEngine.is_card_preserving_enabled = false # Set to true to enable saving payment methods

# IMPORTANT: Configure which model holds the payment integration for each provider.
# Replace "Organization" or "User" with your actual model names.
# This configuration is MANDATORY.
NeetoPaymentsEngine.providers_holdable_class = {
  stripe_standard: "Organization", # e.g., Organization or User that owns the Stripe Standard account
  stripe_platform: "Organization", # Typically Organization that owns the Stripe Platform account
  razorpay: "Organization",        # e.g., Organization or User that owns the Razorpay account
  upi: "Organization"              # Typically Organization that owns the UPI VPA list
}
  • 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.

2. Model Associations

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:

# app/models/organization.rb (Example if Organization is a holdable)
class Organization < ApplicationRecord
  # If Organization holds Stripe Standard Connect account(s)
  has_one :stripe_connected_account, -> { where(payment_provider: :stripe_standard) }, class_name: "::NeetoPaymentsEngine::Stripe::Account", as: :holdable, dependent: :destroy
  # OR if one Org can hold many standard accounts (less common)
  # has_many :stripe_connected_accounts, -> { where(payment_provider: :stripe_standard) }, class_name: "::NeetoPaymentsEngine::Stripe::Account", as: :holdable, dependent: :destroy

  # We have to add this association no matter what because
  # it's internally required by the engine. Change the parent association
  # to Organization/User/Whatever depending on your host app.
  has_one :stripe_platform_account, class_name: "::NeetoPaymentsEngine::Stripe::PlatformAccount", inverse_of: :organization, dependent: :destroy

  # If Organization holds the Razorpay account
  has_one :razorpay_integration, -> { where(payment_provider: :razorpay) }, class_name: "::NeetoPaymentsEngine::Integration", as: :holdable, dependent: :destroy

  # If Organization holds the UPI VPAs list
  has_one :upi_integration, -> { where(payment_provider: :eupi) }, class_name: "::NeetoPaymentsEngine::Integration", as: :holdable, dependent: :destroy

  # General association (optional, can be useful)
  has_many :payment_integrations, class_name: "::NeetoPaymentsEngine::Integration", as: :holdable, dependent: :destroy

  # If Organization itself can have fees associated
  has_one :fee, class_name: "::NeetoPaymentsEngine::Fee", as: :feeable, dependent: :destroy
  has_one :split, class_name: "::NeetoPaymentsEngine::Split", dependent: :destroy # If Org defines platform split %
end

# app/models/user.rb (Example if User is a holdable for Stripe Standard)
class User < ApplicationRecord
  has_one :stripe_connected_account, -> { where(payment_provider: :stripe_standard) }, class_name: "::NeetoPaymentsEngine::Stripe::Account", as: :holdable, dependent: :destroy
  # Add other associations if User can hold other provider accounts
end

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:

# app/models/invoice.rb (Example Payable Model)
class Invoice < ApplicationRecord
  # Association to the payment record for this invoice
  has_one :payment, class_name: "::NeetoPaymentsEngine::Payment", as: :payable, dependent: :destroy

  # Optional: If fees are directly configured on the payable item itself
  has_one :fee, class_name: "::NeetoPaymentsEngine::Fee", as: :feeable, dependent: :destroy
end

3. Secrets and Credentials

Configure API keys and secrets securely using environment variables and secrets file.

Essential ENV variables for neeto-payments-engine:

  • .env.development (Commit this file): Contains non-sensitive defaults, placeholders, or development-specific configurations.

    # .env.development
    
    # Base URL of your host application (adjust port if needed)
    APP_URL='http://app.lvh.me:<APP_PORT>/'
    
    # --- Stripe ---
    # Base URL for OAuth callback (using tunnelto 'connect' subdomain for dev)
    STRIPE_CALLBACK_BASE_URL="https://connect.tunnelto.dev" # Or your tunnel URL
    
    # --- Razorpay (if used) ---
    RAZORPAY_CLIENT_SECRET="rzp_test_..." # Test Key ID can often be committed
    RAZORPAY_CLIENT_ID="..."      # OAuth Client ID is usually safe
    RAZORPAY_WEBHOOK_SECRET=".." # webhook secret
    RAZORPAY_CALLBACK_BASE_URL="https://connect.tunnelto.dev" # Or your tunnel URL
    
  • .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.

    # .env.local (DO NOT COMMIT THIS FILE)
    
    # --- Stripe ---
    STRIPE_SECRET_KEY="sk_test_..." # Your TEST Stripe Secret Key
    STRIPE_WEBHOOK_SECRET="whsec_..." # Your TEST Stripe Webhook Signing Secret (from manual setup)
    # Can find this from Stripe dashboard
    STRIPE_PUBLISHABLE_KEY="pk_test_..."
    # First sign up for Stripe Connect, then go to OAuth section of Stripe
    # Connect to get the client id. You'd also have to register OAuth callback
    # URI in that page.
    STRIPE_CLIENT_ID="ca_..."
    
    # --- Razorpay (if used) ---
    RAZORPAY_KEY_SECRET="..."          # Your TEST Razorpay Key Secret
    RAZORPAY_WEBHOOK_SECRET="..."      # Your TEST Razorpay Webhook Secret (you set this)
    RAZORPAY_CLIENT_SECRET="..."       # Your TEST Razorpay OAuth Client Secret
    
    # --- JWT Keys ---
    # Generate using: openssl genrsa -out private.pem 2048 && openssl rsa -in private.pem -pubout -out public.pem
    # Copy the *entire* content including -----BEGIN...----- and -----END...----- lines.
    CONNECT_PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----
    ...your private key content...
    -----END RSA PRIVATE KEY-----"
    CONNECT_PUBLIC_KEY="-----BEGIN PUBLIC KEY-----
    ...your public key content...
    	-----END PUBLIC KEY-----"
    
    # --- Optional ---
    # OPEN_EXCHANGE_RATE_API_KEY="your_actual_key" # Sensitive API key
    

    Loading Secrets: Ensure these secrets are loaded into Rails.application.secrets. An example config/secrets.yml structure:

    # config/secrets.yml
    default: &default
      host: <%= ENV['APP_URL'] %>
      stripe:
        publishable_key: <%= ENV['STRIPE_PUBLISHABLE_KEY'] %>
        secret_key: <%= ENV['STRIPE_SECRET_KEY'] %>
        client_id: <%= ENV['STRIPE_CLIENT_ID'] %>
        webhooks:
          secret: <%= ENV['STRIPE_WEBHOOK_SECRET'] %>
      razorpay:
        key_id: <%= ENV["RAZORPAY_KEY_ID"] %>
        key_secret: <%= ENV["RAZORPAY_KEY_SECRET"] %>
        client_id: <%= ENV["RAZORPAY_CLIENT_ID"] %>
        client_secret: <%= ENV["RAZORPAY_CLIENT_SECRET"] %>
        oauth_callback_base_url: <%= ENV["RAZORPAY_CALLBACK_BASE_URL"] %>
        webhook:
          secret: <%= ENV["RAZORPAY_WEBHOOK_SECRET"] %>
      jwt:
        connect_private_key: <%= ENV['CONNECT_PRIVATE_KEY'].inspect %>
        connect_public_key: <%= ENV['CONNECT_PUBLIC_KEY'].inspect %>
    
      # Optional: If using Open Exchange Rates for currency conversion
      open_exchange_rates:
          api_key: <%= ENV["OPEN_EXCHANGE_RATE_API_KEY"] %>
      # ... other secrets ...
    
    development:
      <<: *default
      # Development specific overrides
    
    production:
      <<: *default
      # Production specific overrides (use credentials!)
    

    Secrets Management:

    • Development: Use .env.local (which is typically gitignored) to store sensitive keys locally.
    • 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.

4. Stripe Connect Signup

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.

5. OAuth Callback URI Registration

Register the following callback URIs in your respective payment provider dashboards:

  • Stripe: https://<your_connect_subdomain_or_app_domain>/payments/api/v1/public/stripe/oauth/callback
    • <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.
  • Razorpay: https://<your_razorpay_oauth_base_url>/payments/api/v1/public/razorpay/oauth/callback
    • <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.

Frontend Integration

Integrate the React components provided by the @bigbinary/neeto-payments-frontend package.

1. Install Frontend Package

yarn add @bigbinary/neeto-payments-frontend

2. Install Peer Dependencies

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:

# DO NOT INSTALL THE VERSIONS MENTIONED BELOW AS IT MIGHT BE OUTDATED.
# ALWAYS PREFER INSTALLING THE LATEST VERSIONS.
# If something isn't working, then refer to these versions and see
# what's causing the breakage.
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

Note: Carefully manage potential version conflicts with your host application.

3. Components

Import components into your React application as needed.

  • TaxesDashboard (source code)

    Props

    • feeId: The unique identifier for the tax fee.
    • breadcrumbs: Data for rendering the header breadcrumbs.
    • paymentUrl (optional): The URL of the pricing page to redirect users if payments are not enabled.
    • headerSize (optional): Specifies the size of the header. Default size is small
    • noDataHelpText (optional): Help text displayed when there is no data available.
    • onTaxesChange (optional): Callback function triggered after performing actions in the taxes dashboard.
    • titleHelpPopoverProps(optional): Data for the header help popover.

    Usage

    import React from "react";
    import { TaxesDashboard } from "@bigbinary/neeto-payments-frontend";
    
    const App = () => {
      return (
        <TaxesDashboard
          feeId={fee.id}
          breadcrumbs={[{text: "Settings" ,link: routes.admin.form.settings}]}
        />
      );
    };
    
  • PaymentsDashboard (source code)

    Props

    • 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.
    • payableEntityColumns: To specify the columns from the payable entity which need to be additionally displayed. It is an optional prop that defaults to [] if not specified.
    • searchProps: Allows specifying additional columns to be included in the search functionality. By default, only the identifier column from the payments table is searchable.
    • holdableIds: Provide the holdable IDs for each payment provider when the holdable entity is not a User or Organization model.

    Usage

    import React from "react";
    import { PaymentsDashboard } from "@bigbinary/neeto-payments-frontend";
    
    const App = () => {
    
      // neeto-filters-engine search prop syntax.
      const SEARCH_PROPS = {
        node: "bookings:payable.meeting.name",
        model: "Meeting",
        placeholder: "Search by identifier or meeting name",
      };
    
      // Only required if the payment provider holdable modal is not a User or Organization
      const holdableIds = { stripeStandard: formId, razorpay: formId }
    
      const payableEntityColumns =   {
        title: "Submission",
        dataIndex: ["payable", "id"],
        key: "submission",
        ellipsis: true,
        width: "300px",,
      },
    
      return (
        <PaymentsDashboard
          {...{ payableEntityColumns, holdableIds }}
          searchProps={SEARCH_PROPS}
        />
      );
    };
    
  • RefundsDashboard (source code)

    Props

    • payableEntityColumns: To specify the columns from the payable entity which need to be additionally displayed. It is an optional prop that defaults to [] if not specified.
    • searchProps: Allows specifying additional columns to be included in the search functionality. By default, only the identifier column from the refunds table is searchable.

    Usage

    import React from "react";
    import { SplitTransfersDashboard } from "@bigbinary/neeto-payments-frontend";
    
    const App = () => {
    
      // neeto-filters-engine search prop syntax.
      const SEARCH_PROPS = {
        node: "bookings:payable.meeting.name",
        model: "Meeting",
        placeholder: "Search by identifier or meeting name",
      };
    
      const payableEntityColumns =   {
        title: "Submission",
        dataIndex: ["payable", "id"],
        key: "submission",
        ellipsis: true,
        width: "300px",,
      },
    
      return (
        <SplitTransfersDashboard
          {...{ payableEntityColumns }}
          searchProps={SEARCH_PROPS}
        />
      );
    };
    
  • SplitTransfersDashboard (source code)

    Props

    • payableEntityColumns: To specify the columns from the payable entity which need to be additionally displayed. It is an optional prop that defaults to [] if not specified.
    • 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. Usage
    import React from "react";
    import { RefundsDashboard } from "@bigbinary/neeto-payments-frontend";
    
    const App = () => {
    
      // neeto-filters-engine search prop syntax.
      const SEARCH_PROPS = {
        node: "bookings:payable.meeting.name",
        model: "Meeting",
        placeholder: "Search by identifier or meeting name",
      };
    
      const payableEntityColumns =   {
        title: "Submission",
        dataIndex: ["payable", "id"],
        key: "submission",
        ellipsis: true,
        width: "300px",,
      },
    
      return (
        <RefundsDashboard
          {...{ payableEntityColumns }}
          searchProps={SEARCH_PROPS}
        />
      );
    };
    
  • AccountsDashboard (source code)

    Usage

    import React from "react";
    import { AccountsDashboard } from "@bigbinary/neeto-payments-frontend";
    
    const App = () => {
    
      return (<AccountsDashboard />);
    };
    
  • PayoutsDashboard (source code)

    Props

    • isPlatformEnabled: Indicates whether platform integration is enabled.
    • payoutsPageRoute: The route used to display detailed payout information.

    Usage

    import React from "react";
    import { PayoutsDashboard } from "@bigbinary/neeto-payments-frontend";
    
    const App = () => {
    
      return (
        <PayoutsDashboard
          isPlatformEnabled={true}
          payoutsPageRoute={routes.admin.payments.payouts.show}
        />
      );
    };
    
  • RazorpayPaymentButton (source code)

    Props

    • label: The button label. Defaults to Pay.
    • payableId: The ID of the payable entity.
    • discountCode: The discount code to be applied, if any.
    • email: The customer's email address.
    • name: The customer's name.
    • theme: The theme configuration object.
    • payableType: The type of the payable entity.
    • onBeforePayment: A callback function triggered before the payment is initiated.
    • onSuccessfulPayment: A callback function triggered after a successful payment.
    • onFailedPayment: A callback function triggered after a failed payment.

    Usage

    import React from "react";
    import { RazorpayPaymentButton } from "@bigbinary/neeto-payments-frontend";
    
    const App = () => {
    
      return (
        <RazorpayPaymentButton
          email={customer.email}
          name={customer.name}
          discountCode={discountCode.code}
          payableId={booking.id}
          theme={meeting.theme}
        />
      );
    };
    
  • ConfirmUpiPaymentButton (source code)

    Props

    • vpas: A list of UPI IDs presented for the user to select during confirmation.
    • identifier: The unique identifier associated with the payment.
    • paymentId: The ID of the payment.
    • onSuccess: A callback function triggered upon successful confirmation.

    Usage

    import React from "react";
    import { ConfirmUpiPaymentButton } from "@bigbinary/neeto-payments-frontend";
    
    const App = () => {
    
      return (
          <ConfirmUpiPaymentButton
            identifier={payment.identifier}
            payableId={payment.payableId}
            paymentId={payment.paymentId}
            vpas={meeting.fee.vpas}
          />
      );
    };
    

4. Hooks

  • useStripePromise (source code)

    This hook can used to provide the value for the stripe prop of the Stripe Element component.

    Usage

    import React from "react";
    import { useStripePromise } from "@bigbinary/neeto-payments-frontend";
    
    const App = () => {
    
      const stripePromise = useStripePromise({
        stripePlatformAccount // The integration object for the Stripe platform account. No value is required if the platform account integration is not configured.
    
        stripeAccountIdentifier: paymentRecipient?.stripeConnectedAccount?.identifier, // Stripe Standard account integration identifier
      });
    
      return (
          <Elements stripe={stripePromise}>
          </Elements>
      );
    };
    
  • useRazorpayPayment (source code)

    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.

    Usage

    import React from "react";
    import { useRazorpayPayment } from "@bigbinary/neeto-payments-frontend";
    
    const App = () => {
    
      const { makePayment } = useRazorpayPayment({
        payableId, // The ID of the payable entity.
        onBeforePayment, // A callback function triggered before the payment is initiated.
        onSuccessfulPayment, //  A callback function triggered after a successful payment.
        onFailedPayment, // A callback function triggered after a failed payment.
        customAmount, // The custom amount value to be used when a fee of type `range` or `variable` is configured.
    
      });
    
    };
    

5. constants

  • CURRENCY_OPTIONS

    A list of supported currencies in { value, label } format. This can be used as options for the NeetoUI Select component.

6. utils

  • getFormattedAmount({ amount, taxes=[], isTaxEnabled = false})

    Returns the final amount as a string, truncated to 2 decimal places, after applying taxes if isTaxEnabled is set to true and taxes are present.

    Parameter keys

    • amount: The base amount to which taxes may be applied.
    • taxes: List of tax objects with value and kind ("percentage" or "fixed"). Defaults is []
    • isTaxEnabled : Whether taxes should be applied. Defaults to false.

    Returns:

    • string: Final amount with up to 2 decimal places (truncated, not rounded).

    Example:

    getFormattedAmount([{ kind: "percentage", value: 23 }], "39.59", true); // → "48.69"
    
  • getFormattedTaxAmount({ amount, taxes=[]})

    Returns the total tax amount as a string, truncated to 2 decimal places

    Parameter keys

    • amount: The base amount on which tax will be calculated
    • taxes: List of tax objects with value and kind ("percentage" or "fixed"). Defaults is []

    Returns:

    • string: Total tax amount with up to 2 decimal places (truncated, not rounded).

    Example:

    getFormattedTaxAmount([{ kind: "percentage", value: 23 }], "100"); // → "23.00"
    

6. Others

// Example: Payment Settings Page
import React, { useState, useEffect } from 'react';
import axios from 'axios';
import { PaymentsDashboard, StripeConnect } from '@bigbinary/neeto-payments-frontend';
import { useQueryParams } from "@bigbinary/neeto-commons-frontend"; // Assuming you use this hook

const PaymentSettingsPage = () => {
  const [integrations, setIntegrations] = useState({});
  const [isLoading, setIsLoading] = useState(true);
  const [isStripeConnectOpen, setIsStripeConnectOpen] = useState(false);
  const { app: returnedApp, error: oauthError } = useQueryParams(); // For handling OAuth callbacks

  useEffect(() => {
    const fetchIntegrations = async () => {
      setIsLoading(true);
      try {
        // IMPORTANT: Specify ONLY the providers your application actually uses.
        const usedProviders = ["stripe_standard"]; // Example: only using Stripe Standard
        const response = await axios.get('/payments/api/v1/integrations', {
          params: { providers: usedProviders }
        });
        setIntegrations(response.data);
      } catch (err) {
        console.error("Failed to fetch integrations:", err);
        // Handle error appropriately (e.g., show toast notification)
      } finally {
        setIsLoading(false);
      }
    };
    fetchIntegrations();

    // Handle OAuth callback results
    if (returnedApp === 'stripe' && oauthError) {
      console.error("Stripe connection failed:", oauthError);
      // Show error message to user
    } else if (returnedApp === 'stripe') {
      console.log("Stripe connection initiated successfully. Waiting for webhook verification.");
      // Maybe show a success message, fetchIntegrations will update the state eventually
    }
    // Add similar checks for Razorpay if using OAuth

  }, [returnedApp, oauthError]);

  // Determine holdableId based on your app's logic (e.g., current organization)
  // This might come from context, props, or another state hook
  const currentHoldableId = integrations.stripe_standard_account?.holdable_id || 'your_org_or_user_id'; // Replace with actual logic

  if (isLoading) {
    return <div>Loading payment settings...</div>;
  }

  return (
    <div>
      <h1>Payment Settings</h1>

      {/* Conditionally render based on integration status */}
      {!integrations.stripe_standard_account?.is_connected && (
        <button onClick={() => setIsStripeConnectOpen(true)}>
          Connect Stripe Account
        </button>
      )}
      {integrations.stripe_standard_account?.is_connected && (
        <p>Stripe Account Connected: {integrations.stripe_standard_account.identifier}</p>
        // Add disconnect button here
      )}

      {/* Stripe Connect Modal */}
      <StripeConnect
        isOpen={isStripeConnectOpen}
        onClose={() => setIsStripeConnectOpen(false)}
        holdableId={currentHoldableId} // Pass the ID of the Organization/User model instance
        returnUrl={`${window.location.origin}/payment-settings`} // URL Stripe redirects back to (should match registered URI)
        isPlatform={false} // Set true only if connecting a Stripe Platform account
      />

      {/* Example Dashboard (only show if integrated) */}
      {integrations.stripe_standard_account?.is_connected && (
        <>
          <h2>Payments Overview</h2>
          <PaymentsDashboard
            // Example: Use 'connected' kind if viewing standard account payments
            dashboardKind="connected"
            // Pass holdable ID for the specific account being viewed
            holdableIds={{ stripe_standard: currentHoldableId }}
          />
        </>
      )}
    </div>
  );
};

export default PaymentSettingsPage;

4. API Calls - Specify Providers

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.

// Correct: Fetching only Stripe Standard integration status
axios.get('/payments/api/v1/integrations', {
  params: { providers: ["stripe_standard"] }
});

// Incorrect (if not using all providers): Might cause errors
// axios.get('/payments/api/v1/integrations');

Core Concepts

  • 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.
  • Single Table Inheritance (STI): Used for provider-specific models like Payments::Stripe, Payments::Razorpay, Integrations::Razorpay.
  • Service Objects: Encapsulate business logic (e.g., Stripe::Payments::CreateService).
  • Callbacks: Host applications implement methods in NeetoPaymentsEngine::Callbacks to customize behavior. See the Callbacks section.
  • Webhooks: Asynchronous events from providers update payment statuses. See Webhook Handling.
  • JWT Authentication: Secures OAuth callback flows using RSA keys.

Webhook Handling

  • Webhooks are crucial for updating payment statuses asynchronously.
  • Endpoints are available at /payments/api/v1/public/<provider>/webhooks.
  • Signatures are verified using configured secrets.

Development Environment Setup

1. Tunneling

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 and Exposing Tunnelto Locally for more details.

# Example using tunnelto
tunnelto --subdomain connect --port <YOUR_RAILS_APP_PORT>
# This makes https://connect.tunnelto.dev forward to your local app

2. Stripe Webhook Setup (Manual)

  • Go to your Stripe Test Dashboard > Developers > Webhooks.
  • Click "Add endpoint".
  • Endpoint URL: https://connect.tunnelto.dev/payments/api/v1/public/stripe/webhooks (replace with your actual tunnel URL).
  • 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).
  • Click "Add endpoint".
  • After creation, find the Signing secret (starts with whsec_...).
  • Copy this secret and set it as the STRIPE_WEBHOOK_SECRET in your local .env.local or development credentials.
  • 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 for more details.

3. Razorpay Webhook Setup (Manual)

  • Go to your Razorpay Partners Dashboard.
  • Go to Applications
  • Create a new application or open the existing application
  • Add test webhook
  • Webhook URL: https://connect.tunnelto.dev/payments/api/v1/public/razorpay/webhooks (replace with your tunnel URL).
  • Set a Secret (create a strong random string).
  • Select Active Events (e.g., payment.authorized, payment.captured, payment.failed, refund.processed, refund.failed).
  • Save the webhook.
  • Copy the Secret you set and configure it as RAZORPAY_WEBHOOK_SECRET in your local development environment.

Authentication (JWT for OAuth)

  • Secures the OAuth callback flow for connecting Stripe/Razorpay accounts.
  • Uses RSA public/private keys (CONNECT_PUBLIC_KEY, CONNECT_PRIVATE_KEY) configured in your secrets.
  • The ConnectLinkService creates a short-lived JWT containing user context when initiating the OAuth flow.
  • The JwtService verifies the token signature using the public key upon callback.

Callbacks

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.

Mandatory Callbacks

# app/lib/neeto_payments_engine/callbacks.rb
module NeetoPaymentsEngine
  class Callbacks
    # --- Mandatory Callbacks ---

    # Check user permissions for general payment management access.
    # @param user [User] The user attempting the action.
    # @return [Boolean] True if the user has permission.
    def self.can_manage_payments?(user)
      user.has_role?(:admin) # Replace with your authorization logic
    end

    # ... other callbacks ...
  end
end

Optional Callbacks (Implement as needed)

# app/lib/neeto_payments_engine/callbacks.rb
module NeetoPaymentsEngine
  class Callbacks
    # --- Optional Callbacks (Implement as needed) ---

    # Return the fee object applicable to the payable item.
    # @param payable [Object] The host application's payable model instance (e.g., Invoice, Booking).
    # @return [NeetoPaymentsEngine::Fee, nil] The Fee object or nil if no fee applies.
    def self.get_fee(payable)
      # Example: Find fee based on payable's attributes or associated product/service
      payable.applicable_fee # Replace with your actual logic
    end

    # Check if a payment can be processed for this payable item.
    # @param payable [Object] The host application's payable model instance.
    # @return [Boolean] True if payment is allowed, false otherwise.
    def self.payment_processable?(payable)
      # Example: Check if the payable is in a state allowing payment (e.g., 'pending', 'unpaid')
      payable.can_be_paid? # Replace with your actual logic
    end

    # Return an ActiveRecord::Relation scope for payable objects.
    # Used by the engine, e.g., when filtering payments by associated payables.
    # @param organization [Organization] The organization context.
    # @param payable_type [String, nil] The stringified class name of the payable type (e.g., "Invoice"), or nil.
    # @return [ActiveRecord::Relation] A scope of payable objects.
    def self.get_payables(organization, payable_type = nil)
      # Example: Return all Invoices for the organization if type matches
      if payable_type == "Invoice"
        organization.invoices
      elsif payable_type == "Booking"
        organization.bookings
      else
        # Return a sensible default or handle all relevant types
        organization.invoices # Defaulting to invoices here, adjust as needed
      end
    end

    # Validate if a discount code is applicable to a specific payable item.
    # @param discount_code [DiscountCode] Host application's discount code model instance.
    # @param payable [Object] The host application's payable model instance.
    # @return [Boolean] True if the discount code is valid for the payable, false otherwise.
    def self.valid_discount_code?(discount_code, payable)
      # Example: Check code validity, usage limits, applicability to the payable's item/service
      discount_code.is_valid_for?(payable) # Replace with your actual logic
    end

    # Determine if the transaction cookie (used by Stripe flow) should be deleted.
    # Default logic (return true) is usually sufficient. Delete after success/refund.
    # @param payment [NeetoPaymentsEngine::Payment] The payment object.
    # @return [Boolean] True to delete the cookie, false to keep it.
    def self.delete_transaction_cookie?(payment)
      true
    end

    # Return the Stripe connected account associated with the payable's context.
    # Crucial for marketplace payments where funds go to a connected account.
    # @param payable [Object] The host application's payable model instance.
    # @return [NeetoPaymentsEngine::Stripe::Account, nil] The connected account or nil.
    def self.get_connected_account(payable)
      # Example: Find the seller/vendor associated with the payable item
      payable.seller&.stripe_connected_account # Replace with your actual logic
    end

    # Return the Stripe connected account that should receive the split amount for a given payable.
    # Often the same logic as get_connected_account.
    # @param payable [Object] The host application's payable model instance.
    # @return [NeetoPaymentsEngine::Stripe::Account, nil] The destination account for the split.
    def self.get_split_account(payable)
      # Example: Find the recipient/beneficiary for the split payment
      payable.recipient_user&.stripe_connected_account # Replace with your actual logic
    end

    # Return a hash of details for a payable item, used in CSV exports.
    # @param payable [Object] The host application's payable model instance.
    # @return [Hash] Key-value pairs to include in the export.
    def self.get_exportable_details(payable)
      {
        # Example: Extract relevant fields from your payable model
        payable_name: payable.try(:name) || "N/A",
        invoice_number: payable.try(:invoice_number),
        product_sku: payable.try(:product_sku)
      }
    end

    # Return the primary custom hostname for an organization.
    # Used for registering Stripe Payment Method Domains.
    # @param organization [Organization] The organization context.
    # @return [String, nil] The custom hostname or nil.
    def self.custom_domain_hostname(organization)
      # Example: Fetch from a dedicated custom domain model or setting
      organization.custom_domain_setting&.hostname
    end

    # Determine if a payment related to this payable can be split.
    # @param payable [Object] The host application's payable model instance.
    # @return [Boolean] True if splits are allowed for this payable type/context.
    def self.splittable?(payable)
      # Example: Enable splits only for specific product types or services
      payable.allows_splits? # Replace with your actual logic
    end

    # Determine if a split transfer should be initiated immediately or held.
    # @param payable [Object] The host application's payable model instance.
    # @return [Boolean] True to transfer immediately, false to hold (e.g., for review).
    def self.transferable?(payable)
      # Example: Hold transfers for items pending review or fulfillment
      !payable.needs_manual_review? # Replace with your actual logic
    end

    # Return metadata about the payable to include in Stripe transfers.
    # @param payable [Object] The host application's payable model instance.
    # @return [Hash] Metadata hash (keys/values must be strings, limited length).
    def self.get_payable_details(payable)
      {
        payable_id: payable.id.to_s,
        payable_type: payable.class.name,
        # Add other relevant identifiers (e.g., order_id, booking_ref)
        order_reference: payable.try(:order_reference).to_s
      }
    end

    # Check permissions specifically for managing the Stripe Platform account (if applicable).
    # @param user [User] The user attempting the action.
    # @return [Boolean] True if the user has permission.
    def self.can_manage_stripe_platform_account?(user)
      user.has_role?(:super_admin) # Replace with your authorization logic
    end

    # Check permissions specifically for managing Razorpay integrations.
    # @param user [User] The user attempting the action.
    # @return [Boolean] True if the user has permission.
    def self.can_manage_razorpay_integration?(user)
      user.has_role?(:admin) # Replace with your authorization logic
    end
  end
end

Exposed Entities

The engine exposes various models, services, jobs, and tasks for integration and extension:

  • 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.
  • 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.
  • Jobs: Handle background tasks (Stripe::WebhooksJob, StripePlatform::CreatePaymentSplitsJob, ExportCsvJob, CreatePaymentMethodDomainJob). These are queued and processed by Sidekiq.
  • Concerns: Reusable modules (Amountable, PaymentProcessable, Taxable, Stripe::Accountable, Refundable). Mostly for internal engine use.
  • Rake Tasks:
    • neeto_payments_engine:stripe:account:integrate: Seeds a sample Stripe connected account. Development/Testing only.
    • neeto_payments_engine:stripe:webhooks:subscribe: Do not rely on this for setup. See Webhook Handling for manual setup instructions.

API Endpoints

The engine exposes several API endpoints under the configured mount path (default /payments). Here are some key ones:

MethodPathDescriptionAuthentication
GET/api/v1/integrationsList integrated payment provider accounts for the current context.Host App Auth
POST/api/v1/exportsInitiate a CSV export for dashboards (Payments, Refunds, Splits).Host App Auth
GET/api/v1/paymentsList payments with filtering and pagination.Host App Auth
GET/api/v1/refundsList refunds with filtering and pagination.Host App Auth
PUT/api/v1/recurring_payments/:idUpdate the status of an admin-managed recurring payment.Host App Auth
GET/api/v1/split_transfersList split transfers (Stripe Platform) with filtering.Host App Auth
GET/api/v1/split_transfers/:idShow details of a specific split transfer.Host App Auth
POST/api/v1/split_transfers/bulk_updateCancel or resume multiple pending/cancelled split transfers.Host App Auth
GET/api/v1/fees/:fee_id/taxesList taxes configured for a specific fee.Host App Auth
POST/api/v1/fees/:fee_id/taxesCreate a new tax for a fee.Host App Auth
PUT/api/v1/fees/:fee_id/taxes/:idUpdate an existing tax for a fee.Host App Auth
DELETE/api/v1/fees/:fee_id/taxes/:idDelete a tax from a fee.Host App Auth
GET/api/v1/fees/:fee_id/recurring_settingGet recurring payment settings for a fee.Host App Auth
PUT/api/v1/fees/:fee_id/recurring_settingUpdate recurring payment settings for a fee.Host App Auth
GET/api/v1/stripe/countriesList countries supported by Stripe for account creation.Host App Auth
POST/api/v1/stripe/accountsInitiate Stripe connected account creation/onboarding.Host App Auth
GET/api/v1/stripe/accounts/:id/creation_statusCheck the status of an async account creation job.Host App Auth
DELETE/api/v1/stripe/accounts/:idDisconnect a Stripe connected account.Host App Auth
GET/api/v1/stripe_platform/accountGet details of the configured Stripe Platform account.Host App Auth
POST/api/v1/stripe_platform/accountCreate/Connect the Stripe Platform account.Host App Auth
PUT/api/v1/stripe_platform/accountUpdate Stripe Platform account settings.Host App Auth
DELETE/api/v1/stripe_platform/accountDisconnect the Stripe Platform account.Host App Auth
GET/api/v1/stripe_platform/payoutsList payouts made from the Stripe Platform account.Host App Auth
GET/api/v1/razorpay/holdables/:holdable_id/accountGet Razorpay account details for a specific holdable (Deprecated style).Host App Auth
GET/api/v1/upi/vpasList configured UPI VPAs for the organization.Host App Auth
POST/api/v1/upi/vpasAdd a new UPI VPA.Host App Auth
DELETE/api/v1/upi/vpas/:idDelete a UPI VPA.Host App Auth
PUT/api/v1/upi/payments/:idConfirm/update a manual UPI payment (Admin action).Host App Auth
GET/api/v1/integrations/connect/stripeStart Stripe Connect OAuth flow.Host App Auth
GET/api/v1/integrations/connect/razorpayStart Razorpay OAuth flow.Host App Auth
POST/api/v1/public/stripe/transactionsCreate a Stripe Payment Intent (Public endpoint).Open
POST/api/v1/public/razorpay/paymentsCreate a Razorpay Order (Public endpoint).Open
POST/api/v1/public/upi/paymentsInitiate a manual UPI payment (Public endpoint).Open
GET/api/v1/public/recurring_payments/:payable_idGet status of a customer-managed recurring payment.Open
PUT/api/v1/public/recurring_payments/:payable_idAllow customer to cancel their recurring payment.Open
GET/api/v1/public/stripe/oauth/authorizeStripe OAuth authorization step (Redirect handled by engine).JWT
GET/api/v1/public/stripe/oauth/callbackStripe OAuth callback processing endpoint.JWT
GET/api/v1/public/razorpay/oauth/authorizeRazorpay OAuth authorization step (Redirect handled by engine).JWT
GET/api/v1/public/razorpay/oauth/callbackRazorpay OAuth callback processing endpoint.JWT
POST/api/v1/public/stripe/webhooksStripe webhook receiver endpoint.Stripe Sig.
POST/api/v1/public/razorpay/webhooksRazorpay webhook receiver endpoint.Razorpay Sig.
POST/api/v1/public/stripe_platform/webhooksStripe Platform webhook receiver endpoint.Stripe Sig.

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.

Incineration Concern

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).

  • 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.

    # In your host app's IncinerableConcern
    # TODO: Incinerate Fee based on Entity later
    SKIPPED_MODELS = ["NeetoActivitiesEngine::Activity", "NeetoPaymentsEngine::Fee"]
    
  • 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).

  • 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.

    # In your host app's IncinerableConcern
    def self.associated_models(org_id)
      # Add logic for NeetoPaymentsEngine::Fee
      "NeetoPaymentsEngine::Fee": {
        # Example: Find fees associated with Products belonging to the org
        joins: "JOIN products ON products.id = neeto_payments_engine_fees.feeable_id AND neeto_payments_engine_fees.feeable_type = 'Product'",
        where: ["products.organization_id = ?", org_id]
        # Adjust join and where clause based on your actual feeable models
      },
      # Add logic for other NeetoPaymentsEngine models if needed,
      # though many are handled via cascading deletes or direct Org association.
      # Refer to the engine's IncinerableConcern for models it handles itself.
      # "NeetoPaymentsEngine::Payment": { ... }
    
      # rest of the code
    end
    

    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.

Deprecated Patterns

Please be aware of the following deprecations and use the recommended alternatives:

  • Configuration holdable_class:

    • Deprecated: The singular NeetoPaymentsEngine.holdable_class = "ClassName" configuration.
    • Recommended: Use the hash-based NeetoPaymentsEngine.providers_holdable_class = { provider: "ClassName", ... } to configure holdable models per provider. This provides more flexibility.
  • Holdable-Scoped APIs:

    • Deprecated: API endpoints previously located under /api/v1/<provider>/holdables/:holdable_id/... are deprecated.
    • Recommended: Use the newer API structure:
      • For general integration status: /api/v1/integrations (passing the providers param).
      • For provider-specific account management: /api/v1/<provider>/accounts/....
      • For provider-specific resources like payouts: /api/v1/<provider>/payouts/....
  • Charge Model/Concept:

    • Deprecated: Older versions might have referred to payment processing entities as Charge.
    • 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.

Development Environment Setup

To run the engine locally integrated with a host application, ensure the following processes are running:

  • Rails Server: Starts the main web application.
    bundle exec rails s
    
  • Vite Dev Server: Compiles and serves frontend assets.
    # If using Vite
    yarn dev
    
  • Sidekiq Worker: Processes background jobs (like webhook handling).
    bundle exec sidekiq -e development -C config/sidekiq.yml
    
  • Tunneling Service (for Webhooks/OAuth): Exposes your local server to the internet. Use the connect subdomain for OAuth callbacks.
    # Using tunnelto (replace <YOUR_APP_PORT> with your Rails server port, e.g., 3000)
    tunnelto --subdomain connect --port <YOUR_APP_PORT>
    
    Note: The connect subdomain is conventionally used across Neeto applications for receiving callbacks from third-party services like Stripe/Razorpay OAuth.

The Procfile.dev.PAYMENTS might look like this:

web: bundle exec rails s
vite: yarn dev
worker: bundle exec sidekiq -e development -C config/sidekiq.yml
# Tunnel needs to be run in a separate terminal

Helper methods

  • currency_format_with_symbol 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.

    Usage in host application:

    • In general modules (e.g., Services or View helpers)

      module ApplicationHelper
        include ::NeetoPaymentsEngine::CurrencyFormatHelper
      
        def formatted_amount
          currency_format_with_symbol(payment&.amount, payment&.currency)
        end
      end
      
    • In mailers

      • Include the helper in your mailer:
        class Packages::PurchaseConfirmedMailer < ApplicationMailer
          helper ::NeetoPaymentsEngine::CurrencyFormatHelper
        
          def customer_email
          end
        end
        
      • Use the method in the mailer view:
          <%= "#{currency_format_with_symbol(@purchase.payment&.amount,@purchase.payment&.currency)}" %>
        

Testing & Debugging

  • Dummy App: Use the test/dummy app within the engine's repository for isolated testing.
  • Test Helpers: Utilize NeetoPaymentsEngine::TestHelpers (includes HttpRequestHelpers) for stubbing API calls to Stripe/Razorpay (test/helpers/http_request_helpers/).
  • Stripe Test Cards:
    • Valid Card: 4242 4242 4242 4242
    • Declined Card: 4000 0000 0000 0002
    • Expiry Date: Any future date (e.g., 12/30)
    • CVC: Any 3 digits (e.g., 123)
    • ZIP Code: Any valid ZIP (e.g., 12345)
    • More Stripe Test Cards
  • Razorpay Test Cards/UPI: Refer to Razorpay Testing Docs.
  • Logging: Check Rails logs (log/development.log) for detailed output from the engine's LogActivityHelper.
  • Provider Dashboards: Use the Stripe and Razorpay Test Mode dashboards to view API logs, payment details, webhook attempts, and specific error messages.
  • JWT Debugging: Use tools like 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).

Gotchas & Tips

  • providers_holdable_class is Mandatory: Forgetting to configure this in the initializer will lead to errors when the engine tries to find associated accounts.
  • 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).
  • Stripe Connect Signup: You must complete the Stripe Connect signup process in your Stripe account, even for platform-only usage.
  • 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.
  • JWT Key Security: Treat your JWT private key with the same security as your API secret keys.
  • 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.

Publishing

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.

FAQs

Package last updated on 07 Jul 2025

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts