Skip to content

SharmaPawan11/vendure-razorpay-payment-plugin

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

19 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Vendure Razorpay

Vendure Razorpay Plugin

A plugin to enable Razorpay as a payment provider for Vendure E-commerce

Vendure is released under the MIT license. PRs welcome! Follow @vendure_io PRs welcome!

🌟 Feature

This plugin have inside it a lot of stuff:

  • A PaymentMethodHandler to createPayments and configure Razorpay transaction
  • A custom field customFieldsRazorpay_order_id on Order to set razorpayOrderId for corresponding vendure order id.
  • Refund payments in Admin UI

⚙️ Install

1. Install and configure Vendure

Here you can find out how to install

2. Install the package

npm install vendure-razorpay-payment-plugin --save

3. Add the plugin in Vendure configuration

import { RazorpayPlugin } from 'vendure-razorpay-payment-plugin';
const config: VendureConfig = {
  ...
  plugins: [
    RazorpayPlugin
  ]
}

4. Configure RazorPay

You will need to enable and configure the options to make work. You can edit this in Payment Method section in Vendure Admin UI

5. Enjoy!

It's done!

⚙️ Frontend Setup ( Angular )

1. Add razorpay script

<script src="https://checkout.razorpay.com/v1/checkout.js"></script>

or load it on-demand for performance reasons like this.

import { Injectable } from '@angular/core';

@Injectable({
  providedIn: 'root'
})
export class ScriptService {

  constructor() { }

  scripts = [
      {
        name: 'razorpay',
        src: 'https://checkout.razorpay.com/v1/checkout.js'
      },
    ]

  loadScript(name: string) {
    return new Promise((resolve, reject) => {

      const scriptObject = this.scripts.find((script) => {
        if (script.name === name) {
          return script;
        }
        return null;
      });

      if (scriptObject) {
        let script = document.createElement('script');
        script.src = scriptObject.src;
        script.onload = () => {
          resolve(true);
        };
        script.onerror = (error: any) => resolve(false);
        document.getElementsByTagName('head')[0].appendChild(script);
      } else {
        resolve(false);
      }
    });
  }
}
const isScriptLoaded = await this.scriptService.loadScript('razorpay');

2. Set checkout form options accordingly -

private _razorpayOptions = {
  key: environment.razorpayId,
  order_id: '',
  currency: 'INR',
  description: 'Description',
  // image: 'https://s3.amazonaws.com/rzp-mobile/images/rzp.png',
  prefill: {
    email: '',
    contact: '',
    name: '',
  },
  config: {
    display: {
      blocks: {
        card: {
          instruments: [
            {
              method: 'card',
              // issuers: ["UTIB"],
              networks: ['MasterCard', 'Visa', 'RuPay', 'Bajaj Finserv'],
            },
          ],
        },
        upi: {
          name: 'Pay using UPI',
          instruments: [
            {
              method: 'upi',
              flows: ['collect', 'intent', 'qr'],
              apps: ['google_pay', 'bhim', 'paytm', 'phonepe'],
            },
          ],
        },
        netbanking: {
          //  name for other block
          name: 'Pay using netbanking',
          instruments: [
            {
              method: 'netbanking',
            },
          ],
        },
        wallet: {
          name: 'Pay using wallets',
          instruments: [
            {
              method: 'wallet',
              wallets: ['phonepe', 'freecharge', 'airtelmoney'],
            },
          ],
        },
      },
      sequence: [
        'block.card',
        'block.upi',
        'block.netbanking',
        'block.wallet',
      ],
      preferences: {
        show_default_blocks: false,
      },
    },
  },
  handler: function (response: any) {
    console.log(response);
  },
  modal: {
    ondismiss: function () {},
  },
};

3. Generate Razorpay order id by calling mutation from plugin ( Order must be in "ArrangingPayment" state )

razorpayService.ts

const GENERATE_RAZORPAY_ORDER_ID = gql`
  mutation generateRazorpayOrderId($vendureOrderId: ID!) {
    generateRazorpayOrderId(orderId: $vendureOrderId) {
      __typename
      ... on RazorpayOrderIdSuccess {
        razorpayOrderId
      }
      ... on RazorpayOrderIdGenerationError {
        errorCode
        message
      }
    }
  }
  `;

generateRazorpayOrderId(vendureOrderId: string | number) {
  return this.requestor
    .mutate(GENERATE_RAZORPAY_ORDER_ID, {
      vendureOrderId,
    })
    .pipe(map((res) => res.generateRazorpayOrderId)
}

checkout.component.ts

this.orderService.generateRazorpayOrderId(this.orderDetails.id).pipe(
  this.updateOrderDetailsGlobally.operator(),
    takeUntil(this.destroy$)
  )
  .subscribe((res) => {
    this.onRazorpayIdGeneration(res);
});


onRazorpayIdGeneration(res: any) {
  if (res.__typename === 'RazorpayOrderIdSuccess') {
    const razorpayOrderId = res.razorpayOrderId;
    this.openRazorpayPopup(razorpayOrderId); // Implemented below
  } else {
    console.log(
      'Some error occurred while generating Razorpay orderId',
      res.message,
      res.errorCode
    );
    this.razorpayFlowActive = false;
    this.cd.detectChanges();
  }
}

4. Get Razorpay class

get Razorpay() {
  if (!(window as any).Razorpay) {
    throw new Error(
      'Can\'t find razorpay. Make sure you have added <script src="https://checkout.razorpay.com/v1/checkout.js"></script> in your index.html file'
    );
  }
  return (window as any).Razorpay;
}

5. Construct success and manualClose callbacks

    5.1 Get a reference to Angular changeDetector and NgZone

    checkout.component.ts

constructor(
    ...
    private cd: ChangeDetectorRef,
    private zone: NgZone
    ...
  ) {
    ...
  }

    5.2 Construct the callbacks

    checkout.component.ts

onRazorpayPaymentSuccess(metadata: Object) {
  this.cd.detectChanges();
  this.orderService
    .addRazorpayPaymentToOrder(metadata)
    .pipe(
      takeUntil(this.destroy$))
    .subscribe((res) => {
      switch (res.__typename) {
        case 'PaymentFailedError':
        case 'PaymentDeclinedError':
        case 'IneligiblePaymentMethodError':
        case 'OrderPaymentStateError':
          console.log(res.errorCode, res.message);
          break;
        case 'Order':
          console.log('PAYMENT SUCCESSFUL');
          this.zone.run(() => {
            this.router.navigate(['..'], {
              relativeTo: this.route,
            });
          })
      }
    });
}

onRazorpayManualClose() {
  if (confirm('Are you sure, you want to close the form?')) {
    console.log('Checkout form closed by the user');
    this.razorpayFlowActive = false;
    this.cd.detectChanges();
  } else {
    console.log('Complete the Payment');
  }
}

    razorpayService.ts

addRazorpayPaymentToOrder(paymentMetadata: Object): Observable<Mutation["addPaymentToOrder"]> {
  const addPaymentMutationVariable = {
    paymentInput: {
      method: 'razorpay',
      metadata: JSON.stringify(paymentMetadata),
    },
  };
  return this.requestor
    .mutate(
      ADD_PAYMENT_TO_ORDER_MUTATION,
      addPaymentMutationVariable
    )
    .pipe(map((res) => res.addPaymentToOrder));
}

    where ADD_PAYMENT_TO_ORDER_MUTATION is -

const ADD_PAYMENT_TO_ORDER_MUTATION = gql`
  mutation addPaymentToOrder($paymentInput: PaymentInput!) {
    addPaymentToOrder(input: $paymentInput) {
      __typename
      ... on Order {
        id
      }
      ... on PaymentFailedError {
        errorCode
        paymentErrorMessage
        message
      }
      ... on OrderPaymentStateError {
        errorCode
        message
      }
      ... on PaymentDeclinedError {
        message
        paymentErrorMessage
        errorCode
      }
    }
  }
`;

6. Set required things for checkout form options ( Sample code )

razorpayService.ts

get razorpayOptions() {
  return this._razorpayOptions;
}

set razorpayOrderId(orderId: string) {
  this._razorpayOptions.order_id = orderId;
}

set razorpayPrefill({
  email,
  contact,
  name,
}: {email: string | null, contact: string | null, name: string | null}) {
  email = email || '';
  contact = contact || '';
  name = name || '';
  this._razorpayOptions.prefill = {
    email, contact, name
  };
}

checkout.component.ts

openRazorpayPopup(razorpayOrderId: string) {
  try {
    const Razorpay = this.razorpayService.Razorpay;
    this.razorpayService.razorpayOrderId = razorpayOrderId;
    this.razorpayService.razorpayPrefill = {
      contact: this.customerDetails.customerPhNo,
      email: this.customerDetails.customerEmail,
      name: this.customerDetails.customerName,
    };
    this.razorpayService.razorpaySuccessCallback =
      this.onRazorpayPaymentSuccess.bind(this);
    this.razorpayService.razorpayManualCloseCallback =
      this.onRazorpayManualClose.bind(this);
    const rzp = new Razorpay(this.razorpayService.razorpayOptions);
    rzp.on('payment.failed', (response: any) => {
      // console.log(response);
    });
    rzp.open();
  } catch (e) {
    console.log(e);
  }
}

Check razorpay docs here

😍 Do you like?

Please, consider supporting my work as a lot of effort takes place to create this repo! Thanks a lot.

Buy Me A Coffee

❗️ License

MIT

About

Razorpay Plugin for Vendure

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published