# Nimbbl Sonic Checkout SDK

[![npm downloads](https://img.shields.io/npm/dm/nimbbl_sonic)
](https://www.npmjs.com/package/nimbbl_sonic)

Our Checkout library provides a simple integration with your website to accept payments. You can open our checkout either as a popup modal window above your page or can simply redirect to our checkout and get control back using a callback URL.

SAMPLE EXPERIENCE

- To see our checkout in action you can [click here](https://sonicshop.nimbbl.tech) and perform a live transaction.
- Both the popup modal window and redirect experiences are available for you to try out.

## Compatibilities and Dependencies

1. **Modern Browsers**: Works for all modern browsers (Chrome 60+, Firefox 55+, Safari 12+, Edge 79+)
2. **Legacy Browsers**: ES5 compatible version available for Internet Explorer 11, older Android browsers (pre-Android 5.0), and older iOS Safari (pre-iOS 10)
3. **Node.js**: Compatible with Node.js 14.21.3+

## Install NPM pacakge

```js
npm i nimbbl_sonic
```

## Import the Checkout library

Import the Nimbbl's Checkout JSSDK in your HTML file within the `<head>` tag using the `defer` attribute to ensure it loads asynchronously in each place where you intent to launch the checkout.

### For Modern Browsers (Recommended)

#### Using NPM/ES6 Modules

```js
import Checkout from 'nimbbl_sonic';
```

#### Using CDN (Recommended for quick integration)

```html
<!-- Modern browsers - ES6+ compatible -->
<script src="https://cdn.jsdelivr.net/npm/nimbbl_sonic@latest/build/browser/index.js"></script>
```

### For Legacy Browsers (ES5 Compatible)

#### Using NPM/ES6 Modules

```js
import Checkout from 'nimbbl_sonic/es5';
```

#### Using CDN (Recommended for legacy support)

```html
<!-- Legacy browsers - ES5 compatible with polyfills -->
<script src="https://cdn.jsdelivr.net/npm/nimbbl_sonic@latest/build/browser/index.es5.js"></script>
```

### For Direct Browser Usage (Local files)

```html
<script src="node_modules/nimbbl_sonic/build/browser/index.js"></script>
<!-- or for ES5 compatibility -->
<script src="node_modules/nimbbl_sonic/build/browser/index.es5.js"></script>
```

### CDN Benefits

- **Faster loading**: Global CDN with edge locations
- **Caching**: Automatic browser caching for better performance
- **No installation**: No need to install via npm
- **Version management**: Easy to update by changing version number
- **Fallback support**: Automatic fallback to previous versions

### CDN Version Management

#### Latest Version (Development)

```html
<script src="https://cdn.jsdelivr.net/npm/nimbbl_sonic@latest/build/browser/index.js"></script>
```

#### Specific Version (Production - Recommended)

```html
<script src="https://cdn.jsdelivr.net/npm/nimbbl_sonic@3.4.0/build/browser/index.js"></script>
```

#### ES5 Version for Legacy Browsers

```html
<script src="https://cdn.jsdelivr.net/npm/nimbbl_sonic@latest/build/browser/index.es5.js"></script>
```

#### Version Range (Patch Updates Only)

```html
<script src="https://cdn.jsdelivr.net/npm/nimbbl_sonic@^3.4.0/build/browser/index.js"></script>
```

## Initialize Checkout

Initialize the Checkout class by providing your unique `token` obtained from [previous step](/docs/standard-checkout/creating-an-order/#creating-an-order). This step should be performed on the same page where you intend to launch the checkout.

| Option Properties | Type     | Mandatory? | Description                              |
| ----------------- | -------- | ---------- | ---------------------------------------- |
| token             | `string` | `true`     | Token generated from V3 create-order API |

```html
<script>
  const checkout = new Checkout({ token: 'your_token_here' });
</script>
```

## Launching the Nimbbl Checkout

### Understanding Checkout Options

Read more about `options` object and its properties below.

#### Properties related to Callback Mode

Nimbbl provides you with two ways of receiving the response from SDK. This mode also determines the way in which your checkout is launched. One of the mode is, `callback_handler` which launches the Nimbbl Checkout within a popup modal window, alternative to this we have `callback_url` where the Checkout opens in the same window by redirecting users away from your page.

| Option Properties | Type       | Mandatory?                                    | Description                                                                                                                                                                                             |
| ----------------- | ---------- | --------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| callback_handler  | `function` | Required if `callback_url` is not passed.     | Handler Function will open Nimbbl checkout in a popup modal window. On successful or failure payment, customer will get the json response with status.                                                  |
| callback_url      | `string`   | Required if `callback_handler` is not passed. | Checkout opens in the same window by redirecting away from your page. On successful or failure payment, customer will be redirected to the specified Callback URL, for example, a payment success page. |

#### Properties related to Payment Mode

Nimbbl lets you enforce a payment mode on the Checkout. This can be done by passing the `payment_mode_code` property.There are other related properties in to each different payment mode, you can read it in the table below. If you don't pass `payment_mode_code` the consumer is shown the full Checkout with all available payment modes.

| Option Properties | Type     | Mandatory? | Description                                                                                                                                                                                                                                                                                                     |
| ----------------- | -------- | ---------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| payment_mode_code | `string` | `false`    | In case of specific payment mode, this property is mandatory. Possible values `net_banking`, `card`, `upi`, `wallet`. If you don't pass `payment_mode_code` the consumer is shown the full Checkout with all available payment modes.                                                                           |
| bank_code         | `string` | `false`    | Only to be passed in case of `net_banking`. Example: `hdfc`. To get the full list of banks supported for you, please [use this API](/docs/api-reference/list-of-banks-v-3/#list-of-banks-v3). If it isn't passed, Checkout will open with a page for the user to choose their bank from enabled banks           |
| wallet_code       | `string` | `false`    | Only to be passed in case of `wallet`. Example: `jio_money`. To get the full list of wallets supported for you, please [use this API](/docs/api-reference/list-of-wallets-v-3/#list-of-wallets-v3). If it isn't passed, Checkout will open with a page for the user to choose their wallet from enabled wallets |
| payment_flow      | `string` | `false`    | Only to be passed in case of `upi` to determine the flow. Possible values `collect`, `intent`. If it isn't passed, Checkout will open with page for the user to enter their UPI ID or pay using a QR or choose a UPI app                                                                                        |
| upi_id            | `string` | `false`    | Only to be passed in case of `upi` and payment_flow is `collect`. It is the UPI ID of the customer. Example:`wonderwoman@ybl`. Checkout will open with the transaction initiated for the upi id passed in the upi_id field. If it isn't passed, Checkout will open with page for the user to enter their UPI ID |
| upi_app_code      | `string` | `false`    | Only to be passed in case of `upi` and payment_flow is `intent`. Possible values `phonepe`, `gpay`. Checkout will open with the transaction initiated for the upi app passed in the upi_app_code field. If it isn't passed, Checkout will show a UPI QR or prompt user to choose a UPI app                      |

### Setting the Checkout Options

Set all the relevant properties for your use case in the `options` object as shown below

```js
var options = {
  // pass `callback_handler` if you want to open the Checkout in a popup modal.
  callback_handler: function (response) {
    alert(response);
  },

  // only if you want to enforce a specific payment method
  payment_mode_code: string,
  bank_code: string,
  wallet_code: string,
  payment_flow: string,
  upi_id: string,
  upi_app_code: string,
};
```

```js
var options = {
  // pass `callback_url` if you want to open Checkout in the same window.
  callback_url: string,
  redirect: true,

  // only if you want to enforce a specific payment method
  payment_mode_code: string,
  bank_code: string,
  wallet_code: string,
  payment_flow: string,
  upi_id: string,
  upi_app_code: string,
};
```

### Invoking the Checkout

Finally, after setting up the `options` object with respective parameters, we can now invoke checkout by accessing checkout method.

```js
checkout.open(options);
```

## Example Usages

### CDN Integration Examples (No NPM Required)

#### CDN Example 1: Modern Browser Integration

```html
<!DOCTYPE html>
<html>
  <head>
    <title>Nimbbl Checkout - CDN Integration</title>
    <!-- Load Nimbbl SDK from CDN -->
    <script src="https://cdn.jsdelivr.net/npm/nimbbl_sonic@latest/build/browser/index.js"></script>
  </head>
  <body>
    <button onclick="openCheckout()">Pay Now</button>

    <script>
      // Initialize checkout with your token
      const checkout = new Checkout({
        token: 'your_order_token_here',
      });

      function openCheckout() {
        const options = {
          callback_handler: function (response) {
            console.log('Payment Response:', response);
            if (response.status === 'success') {
              alert('Payment successful! Transaction ID: ' + response.transaction_id);
            } else {
              alert('Payment failed: ' + response.message);
            }
          },
        };

        checkout.open(options);
      }
    </script>
  </body>
</html>
```

#### CDN Example 2: Legacy Browser Support (ES5)

```html
<!DOCTYPE html>
<html>
  <head>
    <title>Nimbbl Checkout - ES5 CDN Integration</title>
    <!-- Load Nimbbl SDK ES5 version from CDN -->
    <script src="https://cdn.jsdelivr.net/npm/nimbbl_sonic@latest/build/browser/index.es5.js"></script>
  </head>
  <body>
    <button onclick="openCheckout()">Pay Now</button>

    <script>
      // Initialize checkout with your token
      var checkout = new Checkout({
        token: 'your_order_token_here',
      });

      function openCheckout() {
        var options = {
          callback_handler: function (response) {
            console.log('Payment Response:', response);
            if (response.status === 'success') {
              alert('Payment successful! Transaction ID: ' + response.transaction_id);
            } else {
              alert('Payment failed: ' + response.message);
            }
          },
        };

        checkout.open(options);
      }
    </script>
  </body>
</html>
```

#### CDN Example 3: Specific Version (Recommended for Production)

```html
<!DOCTYPE html>
<html>
  <head>
    <title>Nimbbl Checkout - Versioned CDN</title>
    <!-- Load specific version for stability -->
    <script src="https://cdn.jsdelivr.net/npm/nimbbl_sonic@3.4.0/build/browser/index.js"></script>
  </head>
  <body>
    <button onclick="openCheckout()">Pay Now</button>

    <script>
      const checkout = new Checkout({
        token: 'your_order_token_here',
      });

      function openCheckout() {
        const options = {
          callback_url: 'https://yourwebsite.com/payment-success',
        };

        checkout.open(options);
      }
    </script>
  </body>
</html>
```

### NPM Integration Examples

#### 1. Simple Popup Modal Checkout (All Payment Methods)

```html
<!DOCTYPE html>
<html>
  <head>
    <title>Nimbbl Checkout Example</title>
  </head>
  <body>
    <button onclick="openCheckout()">Pay Now</button>

    <script type="module">
      import Checkout from 'nimbbl_sonic';

      // Initialize checkout with your token
      const checkout = new Checkout({
        token: 'your_order_token_here',
      });

      function openCheckout() {
        const options = {
          callback_handler: function (response) {
            console.log('Payment Response:', response);
            if (response.status === 'success') {
              alert('Payment successful! Transaction ID: ' + response.transaction_id);
            } else {
              alert('Payment failed: ' + response.message);
            }
          },
          redirect: false,
        };

        checkout.open(options);
      }
    </script>
  </body>
</html>
```

#### 2. Redirect Checkout (All Payment Methods)

```html
<!DOCTYPE html>
<html>
  <head>
    <title>Nimbbl Checkout Example</title>
  </head>
  <body>
    <button onclick="openCheckout()">Pay Now</button>

    <script type="module">
      import Checkout from 'nimbbl_sonic';

      const checkout = new Checkout({
        token: 'your_order_token_here',
      });

      function openCheckout() {
        const options = {
          callback_url: 'https://yourwebsite.com/payment-success',
        };

        checkout.open(options);
      }
    </script>
  </body>
</html>
```

### Payment Method Specific Examples

#### 3. UPI Payment Only

```javascript
import Checkout from 'nimbbl_sonic';

const checkout = new Checkout({
  token: 'your_order_token_here',
});

const options = {
  callback_handler: function (response) {
    console.log('UPI Payment Response:', response);
  },
  payment_mode_code: 'upi',
  payment_flow: 'collect', // or 'intent'
  upi_id: 'customer@upi', // optional, for collect flow
};

checkout.open(options);
```

#### 4. Net Banking (Specific Bank)

```javascript
import Checkout from 'nimbbl_sonic';

const checkout = new Checkout({
  token: 'your_order_token_here',
});

const options = {
  callback_handler: function (response) {
    console.log('Net Banking Response:', response);
  },
  payment_mode_code: 'net_banking',
  bank_code: 'hdfc', // HDFC Bank
};

checkout.open(options);
```

#### 5. Credit/Debit Card Only

```javascript
import Checkout from 'nimbbl_sonic';

const checkout = new Checkout({
  token: 'your_order_token_here',
});

const options = {
  callback_handler: function (response) {
    console.log('Card Payment Response:', response);
  },
  payment_mode_code: 'card',
};

checkout.open(options);
```

#### 6. Wallet Payment

```javascript
import Checkout from 'nimbbl_sonic';

const checkout = new Checkout({
  token: 'your_order_token_here',
});

const options = {
  callback_handler: function (response) {
    console.log('Wallet Payment Response:', response);
  },
  payment_mode_code: 'wallet',
  wallet_code: 'jio_money', // Jio Money
};

checkout.open(options);
```

### Advanced Examples

#### 7. React Component Example

##### Using NPM (Recommended for React projects)

```jsx
import React, { useEffect } from 'react';
import Checkout from 'nimbbl_sonic';

const PaymentComponent = ({ orderToken }) => {
  useEffect(() => {
    const checkout = new Checkout({ token: orderToken });

    const handlePayment = () => {
      const options = {
        callback_handler: (response) => {
          if (response.status === 'success') {
            console.log('Payment successful:', response);
            // Handle success
          } else {
            console.log('Payment failed:', response);
            // Handle failure
          }
        },
        redirect: false,
      };

      checkout.open(options);
    };

    // Attach to button or call directly
    window.handlePayment = handlePayment;
  }, [orderToken]);

  return <button onClick={() => window.handlePayment()}>Pay Now</button>;
};

export default PaymentComponent;
```

##### Using CDN in React (Alternative approach)

```jsx
import React, { useEffect } from 'react';

const PaymentComponent = ({ orderToken }) => {
  useEffect(() => {
    // Load Nimbbl SDK from CDN
    const script = document.createElement('script');
    script.src = 'https://cdn.jsdelivr.net/npm/nimbbl_sonic@latest/build/browser/index.js';
    script.onload = () => {
      const checkout = new window.Checkout({ token: orderToken });

      const handlePayment = () => {
        const options = {
          callback_handler: (response) => {
            if (response.status === 'success') {
              console.log('Payment successful:', response);
              // Handle success
            } else {
              console.log('Payment failed:', response);
              // Handle failure
            }
          },
          redirect: false,
        };

        checkout.open(options);
      };

      window.handlePayment = handlePayment;
    };
    document.head.appendChild(script);

    return () => {
      // Cleanup script on unmount
      const existingScript = document.querySelector('script[src*="nimbbl_sonic"]');
      if (existingScript) {
        existingScript.remove();
      }
    };
  }, [orderToken]);

  return <button onClick={() => window.handlePayment()}>Pay Now</button>;
};

export default PaymentComponent;
```

#### 8. Vue.js Component Example

```vue
<template>
  <button @click="openCheckout">Pay Now</button>
</template>

<script>
import Checkout from 'nimbbl_sonic';

export default {
  name: 'PaymentComponent',
  props: {
    orderToken: {
      type: String,
      required: true,
    },
  },
  methods: {
    openCheckout() {
      const checkout = new Checkout({
        token: this.orderToken,
      });

      const options = {
        callback_handler: (response) => {
          if (response.status === 'success') {
            this.$emit('payment-success', response);
          } else {
            this.$emit('payment-failed', response);
          }
        },
        redirect: false,
      };

      checkout.open(options);
    },
  },
};
</script>
```

#### 9. Angular Component Example

```typescript
import { Component, Input } from '@angular/core';
import Checkout from 'nimbbl_sonic';

@Component({
  selector: 'app-payment',
  template: '<button (click)="openCheckout()">Pay Now</button>',
})
export class PaymentComponent {
  @Input() orderToken: string = '';

  openCheckout() {
    const checkout = new Checkout({
      token: this.orderToken,
    });

    const options = {
      callback_handler: (response: any) => {
        if (response.status === 'success') {
          console.log('Payment successful:', response);
        } else {
          console.log('Payment failed:', response);
        }
      },
      redirect: false,
    };

    checkout.open(options);
  }
}
```

#### 10. ES5 Compatible Example (Legacy Browsers)

```html
<!DOCTYPE html>
<html>
  <head>
    <title>Nimbbl Checkout - ES5 Compatible</title>
  </head>
  <body>
    <button onclick="openCheckout()">Pay Now</button>

    <script src="node_modules/nimbbl_sonic/build/browser/index.es5.js"></script>
    <script>
      // For ES5 compatibility, use the global NimbblSDK
      var checkout = new NimbblSDK.Checkout({
        token: 'your_order_token_here',
      });

      function openCheckout() {
        var options = {
          callback_handler: function (response) {
            console.log('Payment Response:', response);
            if (response.status === 'success') {
              alert('Payment successful!');
            } else {
              alert('Payment failed: ' + response.message);
            }
          },
          redirect: false,
        };

        checkout.open(options);
      }
    </script>
  </body>
</html>
```

### Response Handling Examples

#### 11. Detailed Response Handler

```javascript
import Checkout from 'nimbbl_sonic';

const checkout = new Checkout({
  token: 'your_order_token_here',
});

const options = {
  callback_handler: function (response) {
    console.log('Full Response:', response);

    switch (response.status) {
      case 'success':
        console.log('Transaction ID:', response.transaction_id);
        console.log('Order ID:', response.order_id);
        console.log('Amount:', response.amount);
        console.log('Payment Method:', response.payment_mode);

        // Send to your server for verification
        verifyPaymentOnServer(response);
        break;

      case 'failed':
        console.log('Error Code:', response.error_code);
        console.log('Error Message:', response.message);
        console.log('Failure Reason:', response.failure_reason);

        // Handle payment failure
        handlePaymentFailure(response);
        break;

      case 'cancelled':
        console.log('Payment was cancelled by user');
        // Handle cancellation
        handlePaymentCancellation();
        break;

      default:
        console.log('Unknown status:', response.status);
    }
  },
  redirect: false,
};

function verifyPaymentOnServer(response) {
  // Send response to your server for verification
  fetch('/api/verify-payment', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify(response),
  })
    .then((result) => result.json())
    .then((data) => {
      if (data.verified) {
        alert('Payment verified successfully!');
      } else {
        alert('Payment verification failed!');
      }
    })
    .catch((error) => {
      console.error('Verification error:', error);
    });
}

function handlePaymentFailure(response) {
  alert('Payment failed: ' + response.message);
  // Show retry options or redirect to error page
}

function handlePaymentCancellation() {
  alert('Payment was cancelled');
  // Redirect to cart or show cancellation message
}

checkout.open(options);
```

## Capture Transaction Response

The final step in completing the client integration is capturing the response and sending it to your server

#### Checkout with Handler Function

If you used the sample code with the handler function, then:

```js
"handler": function (response){
    // Process the response here and
    // send  the response to your server for processing
}
```

#### Checkout with Callback URL

Provided below is a detailed example that demonstrates all the code that you need to do to accept payments on your web page. Please note this example covers the popup modal flow using the `callback_handler`.

`callback_url?response=base64_payload`

You will need to decode the base64 payload. [To learn how to decode the payload, you can refer here](https://www.base64decode.org/). The decoded response will need to be sent to your server.

IMPORTANT

- Share the response parameters received to your server
- This will need to be validated on your server before you decide to provide goods or services
- More details available on processing the response in [Completing the Integration](/docs/standard-checkout/completing-integration/)

## Development Workflow

This project includes automated quality checks to ensure code quality before pushing to the repository.

### Pre-push Validation

The project uses **Husky** to automatically run quality checks before pushing code:

- **Pre-commit**: Automatically formats code and runs linting on staged files
- **Pre-push**: Runs linting, tests, and build validation before pushing to remote

### Manual Pre-push Check

You can also run the validation manually before pushing:

```bash
# Run all pre-push checks manually
npm run pre-push

# Or run the script directly
bash scripts/pre-push-check.sh
```

### What Gets Checked

1. **Linting**: ESLint checks for code style and potential issues
2. **Tests**: Jest runs all unit tests to ensure functionality
3. **Build Validation**: Ensures the project builds successfully
4. **Bundle Size**: Checks if the bundle size is within acceptable limits

### CI/CD Pipeline

The project uses **Bitbucket Pipelines** for continuous integration:

- Runs on every push and pull request
- Automatically validates linting, tests, and builds
- Provides build artifacts for deployment

### Development Commands

```bash
# Install dependencies
npm install

# Run linting
npm run lint

# Fix linting issues automatically
npm run lint:fix

# Run tests
npm run test

# Run tests with coverage
npm run test:coverage

# Build for development
npm run build:dev

# Build for production
npm run build:prod

# Run pre-push validation
npm run pre-push
```

### Troubleshooting

If pre-push checks fail:

1. **Linting errors**: Run `npm run lint:fix` to auto-fix most issues
2. **Test failures**: Check the test output and fix failing tests
3. **Build errors**: Ensure all imports are correct and dependencies are installed

## Contribution

Contributions are always welcome! If you have any suggestions or issues, please open an issue or a pull request.

## License

This project is licensed under the (ISC) license.

## Test Coverage

The following test coverage was generated from the latest test run:

| Metric     | Coverage | Covered / Total |
| ---------- | -------- | --------------- |
| Statements | 82.26%   | 1874 / 2278     |
| Branches   | 75.12%   | 148 / 197       |
| Functions  | 86.66%   | 65 / 75         |
| Lines      | 82.26%   | 1874 / 2278     |

> To update this table, run `npm run test -- --coverage` and copy the summary here.
