Coder Social home page Coder Social logo

brendanbond / react-acceptjs Goto Github PK

View Code? Open in Web Editor NEW
16.0 2.0 5.0 466 KB

A modern React implementation of the Authorize.net platform's Accept.JS library for easily submitting payments to the Authorize.net platform.

HTML 4.81% TypeScript 83.17% CSS 2.70% JavaScript 9.32%

react-acceptjs's Introduction

react-acceptjs

A lightweight modern React implementation of Authorize.net's Accept.JS library for easily submitting payments to the Authorize.net platform.

NPM JavaScript Style Guide

Install

# install with npm
npm install --save react-acceptjs

# install with yarn
yarn add react-acceptjs

Getting Started

Per Authorize.net's Accept.js documentation, there are three options for sending secure payment data to the Authorize.net platform (rather than transmitting sensitive credit card data to your server). You can follow along by building and running the example app located in the example directory.

Please note that Accept.js and Authorize.net require an HTTPS connection.

  1. Host your own payment form and use the dispatchData() function exposed by the useAcceptJs() hook. This function returns a payment nonce which can be used by your server to process a payment in place of CC or bank account data.

    import { useAcceptJs } from 'react-acceptjs';
    
    const authData = {
      apiLoginID: 'YOUR AUTHORIZE.NET API LOGIN ID',
      clientKey: 'YOUR AUTHORIZE.NET PUBLIC CLIENT KEY',
    };
    
    type BasicCardInfo = {
      cardNumber: string;
      cardCode: string;
      month: string;
      year: string;
    };
    
    const App = () => {
      const { dispatchData, loading, error } = useAcceptJs({ authData });
      const [cardData, setCardData] = React.useState<BasicCardInfo>({
        cardNumber: '',
        month: '',
        year: '',
        cardCode: '',
      });
    
      const handleSubmit = async (event) => {
        event.preventDefault();
        // Dispatch CC data to Authorize.net and receive payment nonce for use on your server
        const response = await dispatchData({ cardData });
        console.log('Received response:', response);
      };
    
      return (
        <form onSubmit={handleSubmit}>
          <input
            type="text"
            name="cardNumber"
            value={cardData.cardNumber}
            onChange={(event) =>
              setCardData({ ...cardData, cardNumber: event.target.value })
            }
          />
          <input
            type="text"
            name="month"
            value={cardData.month}
            onChange={(event) =>
              setCardData({ ...cardData, month: event.target.value })
            }
          />
          <input
            type="text"
            name="year"
            value={cardData.year}
            onChange={(event) =>
              setCardData({ ...cardData, year: event.target.value })
            }
          />
          <input
            type="text"
            name="cardCode"
            value={cardData.cardCode}
            onChange={(event) =>
              setCardData({ ...cardData, cardCode: event.target.value })
            }
          />
          <button type="submit" disabled={loading || error}>
            Pay
          </button>
        </form>
      );
    };
    
    export default App;
  2. Embed the hosted, mobile-optimized payment information form provided by Accept.js into your page via the HostedForm component. This component renders a button which, when clicked, will trigger a lightbox modal containing the hosted Accept.js form. You'll still receive the payment nonce for use on your server similar to option #1.

    import { HostedForm } from 'react-acceptjs';
    
    const authData = {
      apiLoginID: 'YOUR AUTHORIZE.NET API LOGIN ID',
      clientKey: 'YOUR AUTHORIZE.NET PUBLIC CLIENT KEY',
    };
    
    const App = () => {
      const handleSubmit = (response) => {
        console.log('Received response:', response);
      };
      return <HostedForm authData={authData} onSubmit={handleSubmit} />;
    };
    
    export default App;
  3. Use Accept Hosted, Authorize.net's fully hosted payment solution that you can redirect your customers to or embed as an iFrame within your page. First, your server will make a request to the getHostedPaymentPageRequest API and receive a form token in return. Next, you'll pass this form token to the <AcceptHosted /> component. Rather than return a payment nonce for use on your server, Authorize.net will handle the entire transaction process based on options you specify in the getHostedPaymentPageRequest API call and return a response indicating success or failure and transaction information.

    1. Redirect your customers to the Accept Hosted form:

      import { AcceptHosted } from 'react-acceptjs';
      
      const App = ({ formToken }: { formToken: string | null }) => {
        return formToken ? (
          <AcceptHosted formToken={formToken} integration="redirect">
            Continue to Redirect
          </AcceptHosted>
        ) : (
          <div>
            You must have a form token. Have you made a call to the
            getHostedPaymentPageRequestAPI?
          </div>
        );
      };
      
      export default App;
    2. Embed the Accept Hosted form as in iFrame lightbox modal:

      1. You'll need to host an JavaScript page that can receive messages from the Accept Hosted iFrame on the same domain as your app with the code below. You should pass this URL as the hostedPaymentIFrameCommunicatorUrl option in the getHostedPaymentPageRequest request you make to receive your form token. For example, in a React app created with Create-React-App, you could put this file into the public/ directory in order to be accessible to Accept Hosted, or place the <script /> tag directly into the public/index.html file. Just be sure that the URL that you pass to getHostedPaymentPageRequest matches where this script is hosted.

        <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
        <html xmlns="http://www.w3.org/1999/xhtml">
          <head>
            <title>Iframe Communicator</title>
            <script type="text/javascript">
              //<![CDATA[
              function callParentFunction(str) {
                if (
                  str &&
                  str.length > 0 &&
                  window.parent &&
                  window.parent.parent &&
                  window.parent.parent.AuthorizeNetIFrame &&
                  window.parent.parent.AuthorizeNetIFrame
                    .onReceiveCommunication
                ) {
                  // Errors indicate a mismatch in domain between the page containing the iframe and this page.
                  window.parent.parent.AuthorizeNetIFrame.onReceiveCommunication(
                    str
                  );
                }
              }
        
              function receiveMessage(event) {
                if (event && event.data) {
                  callParentFunction(event.data);
                }
              }
        
              if (window.addEventListener) {
                window.addEventListener('message', receiveMessage, false);
              } else if (window.attachEvent) {
                window.attachEvent('onmessage', receiveMessage);
              }
        
              if (window.location.hash && window.location.hash.length > 1) {
                callParentFunction(window.location.hash.substring(1));
              }
              //]]/>
            </script>
            <meta name="robots" content="noindex,nofollow" />
          <body></body>
        </html>
      2. Now you can use the <AcceptHosted /> component.

        const App = ({ formToken }: { formToken: string | null }) => {
          return formToken ? (
            <AcceptHosted
              formToken={formToken}
              integration="iframe"
              onTransactionResponse={(response) =>
                setResponse(JSON.stringify(response, null, 2) + '\n')
              }
            >
              <AcceptHosted.Button className="btn btn-primary">
                Continue to IFrame
              </AcceptHosted.Button>
              <AcceptHosted.IFrameBackdrop />
              <AcceptHosted.IFrameContainer>
                <AcceptHosted.IFrame />
              </AcceptHosted.IFrameContainer>
            </AcceptHosted>
          ) : (
            <div>
              You must have a form token. Have you made a call to the
              getHostedPaymentPageRequestAPI?
            </div>
          );
        };
        
        export default App;
      3. A note on styling: a goal of this library is to invert control of the UI to the developer. Each of the <AcceptHosted /> compound compontents (<Button />, <IFrameBackdrop />, <IFrameContainer />, and <IFrame />) have both className and style object props, allowing the default styles to be overridden. At this moment, this is not recommended.

API Reference

Hook

const { dispatchData, loading, error } = useAcceptJs({ environment, authData });

Description

A React hook that loads the appropriate Accept.js script and exposes the dispatchData() function for retrieving a payment nonce for use on your server.

Arguments:

  • authData : { clientKey: string; apiLoginId: string; } - Required. Your Authorize.net client key and API login ID.
  • environment : 'SANDBOX' | 'PRODUCTION' - Optional, defaults to 'SANDBOX'. Indicates whether you are running a sandbox or a production Authorize.net account.

Return Value:

  • dispatchData : (paymentData: { PaymentData }) => Promise<DispatchDataResponse> - Sends your payment form's payment information to Authorize.net in exchange for a payment nonce for use on your server. If you're transmitting credit card data, the PaymentData type will consist of:
type PaymentData = {
  cardData: {
    cardNumber: string;
    month: string;
    year: string;
    cardCode: string;
  };
};

If you're transmitting bank account data, the PaymentData type will instead consist of:

type PaymentData = {
  bankData: {
    accountNumber: string;
    routingNumber: string;
    nameOnAccount: string;
    accountType: 'checking' | 'savings' | 'businessChecking';
  };
};

The dispatchData() function will return a value of type DispatchDataResponse, which will consist of either your payment nonce (referred to as opaqueData) for use in processing the transaction or an error message:

type DispatchDataResponse = {
  opaqueData: {
    dataDescriptor: string;
    dataValue: string;
  };
  messages: {
    resultCode: 'Ok' | 'Error';
    message: ErrorMessage[];
  };
};
  • loading : boolean - Indicates whether the Accept.js library is currently loading.
  • error : boolean - Indicates whether an error has occured while loading the Accept.js library.

Components

<HostedForm />

<HostedForm authData={authData} onSubmit={handleSubmit} />

Description

A React component that loads the appropriate Accept.js script and renders a button that will trigger a hosted PCI-DSS SAQ A compliant form that, when submitted, will return a payment nonce for use on your server.

Props

  • authData : { clientKey: string; apiLoginId: string; } - Required. Your Authorize.net client key and API login ID.
  • onSubmit : (response: HostedFormDispatchDataFnResponse) => void - Required. The function that will receive and handle the response from Authorize.net (which, if successful, will include the payment nonce as well as certain encrypted CC information).
  • environment : 'SANDBOX' | 'PRODUCTION' - Optional, defaults to 'SANDBOX'. Indicates whether you're running a sandbox or production Authorize.net account.
  • billingAddressOptions : { show: boolean; required: boolean } - Optional, defaults to { show: true, required: true }. Indicates whether the hosted form will display and/or require billing information.
  • buttonText : string - Optional, defaults to "Pay". The text that will appear on the button rendered by the component.
  • formButtonText : string - Optional, defaults to "Pay". The text that will appear on the hosted payment form's submit button.
  • formHeaderText : string - Optional, defaults to "Pay". The text that will appear as a header on the hosted payment form.
  • paymentOptions : { showCreditCard: boolean, showBankAccount: boolean } - Optional, defaults to { showCreditCard: true, showBankAccount: false }. What payment options the hosted form will provide.
  • buttonStyle : React.CSSProperties - Optional, defaults to undefined. A style object for the payment button.
  • errorTextStyle : React.CSSProperties - Optional, defaults to undefined. A style object for the error text that displays under the payment button on error.
  • containerStyle : React.CSSProperties - Optional, defaults to undefined. A style object for the \<div /\> that contains the rendered button and error text.

<AcceptHosted />

<AcceptHosted formToken={formToken} integraton="redirect">
  {children}
</AcceptHosted>
<AcceptHosted
  formToken={formToken}
  integration="iframe"
  onTransactionResponse={handleTransactionResponse}
/>

Description

A React component that will render Authorize.net's fully hosted PCI-DSS SAQ A compliant payment solution. Can be rendered either as a redirect (i.e. the user clicks a button and is redirected to the form page) or as an embedded iFrame on your payment page.

Props

  • formToken : string - Required. The form token returned by the Authorize.net getHostedPaymentPageRequest API. See above for details.
  • integration : 'iframe' | 'redirect' - Required. How the hosted payment form is rendered and displayed to the user (i.e. as a redirect or as an embedded iframe).
  • onTransactionResponse : (response: AcceptHostedTransactionResponse) => void; - Required ("iframe" initegration). Callback function for a successful transaction response from Authorize.net. Please note that you must set showReceipt to false in the hostedPaymentReturnOptions of the getHostedPaymentPageRequest API call to receive a transaction response.
  • environment : 'SANDBOX' | 'PRODUCTION' - Optional, defaults to 'SANDBOX'. Indicates whether you're running a sandbox or production Authorize.net account.
  • onCancel : () => void; - Optional ("iframe" integration). Callback function for a user-initiated cancel event (i.e. the user clicks the "Cancel" button on the hosted form).
  • onSuccessfulSave : () => void - Optional ("iframe" integration). For "iframe" integration only. Callback function for a "successful save," which may be deprecated as the Authorize.net documentation doesn't specify what this is.
  • onResize : (width: number, height: number) => void - Optional ("iframe" integration). For "iframe" integration only. Callback function for a "resize" event, in which Authorize.net will suggest a new width and height for the embedded iFrame.
  • children : React.ReactNode - Required ("redirect" integration). The content of the button that will trigger the redirect to the hosted payment form.
  • children : React.ReactNode - Required ("iframe" integration). The content of the button that will trigger the iframe that contains the hosted payment form, as well as the components that style and layout the iframe lightbox (see below).
<AcceptHosted.Button>
  Continue to IFrame
</AcceptHosted.Button>
<AcceptHosted.IFrameBackdrop />
<AcceptHosted.IFrameContainer>
  <AcceptHosted.IFrame />
</AcceptHosted.IFrameContainer>

Description

The compound components that control the layout/styling of the embedded iFrame. <AcceptHosted.Button /> will render the button that will trigger the lightbox modal, <AcceptHosted.IFrameBackdrop /> will render the backdrop behind the lightbox, <AcceptHosted.IFrameContainer /> wraps the iFrame content, and <AcceptHosted.IFrame /> contains the iFrame itself. These components cannot be rendered outside of an <AcceptHosted /> component.

Alternatives

For a similar library with opinionated styles, check out react-authorizenet.

License

MIT © brendanbond

react-acceptjs's People

Contributors

brendanbond avatar sbamniya avatar tylerjohanson avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

react-acceptjs's Issues

Adding key on hosted form stops opening payment modal

I have implemented this library in Next.js app. This works fine, as a standalone page. I am having an issue, as I have 5 options in my page to select from, the problem I am having is, even if I change the selection from sidebar, the modal doesn't get updated state. I am sure this is not an issue with React implementation, as same is working with other Payment provider like Stripe and PayPal. I tried adding key to the component so it gets recreated on change, but when I add key, it stops opening modal. If I remove it, it works correctly.

<HostedForm
    buttonClassName={clsx(
      "use-focus-outline",
      "flex grow cursor-pointer items-center",
      "rounded-full xl:rounded-10",
      "px-7",
      "h-10 xs:h-11 xl:h-9",
      "xl:min-w-[22.5rem] 3xl:min-w-[18rem]",
      selectedPaymentMethod !== "paypal"
        ? "bg-my-purple text-white"
        : "bg-[#d8d8d8] dark:bg-[#707070]"
    )}
    buttonText="Pay with Authorize.net"
    formHeaderText="Complete your purchase"
    authData={authData}
    onSubmit={(response: any) => {
      handleSubmit(response);
    }}
    environment="PRODUCTION"
    key={tokenPackage}
  />

Onsubmit is calling twice in HostedForm

<HostedForm authData={clientSecret}
                        onSubmit={handleSubmit}
                        errorTextStyle={{ fontSize: "12px", color: "red" }}
                        buttonText={t("affiliate.buyNow")}
                        buttonClassName={style.buy_button}
                        disabled={false}
                        onCancel={(e) => onWindowCncel(e)}
                        environment="SANDBOX"
                      />

Onsubmit is calling handleSubmit function twice how can i prevent this issue ?

HostedForm is not showing up

I have observed that when the HostedForm is properly working, you should see the code existing inside the body of the page when you inspect its element. In our system, we are using react with material UI and placing the HostedForm inside the material Drawer, When I close the drawer and re-open it, the on-click function of the HostedForm form does not work anymore. It seems like the functionality that adds the class="show" to the element that makes the form appear is not working.

Expiry Date Problem

Always showing { code: "E_WC_07", text: "Please provide valid expiration year." } and { code: "E_WC_06", text: "Please provide valid expiration month." } errors.

Please help me.

Styling the Accept Hosted form?

We are currently trying to use the accept hosted implementation from your lib and are failing to style it, so I wanted to generally know, what options are even possible here? What is the recommended implementation?
In point 3 you are writing:

A note on styling: a goal of this library is to invert control of the UI to the developer. Each of the <AcceptHosted /> compound compontents (<Button />, <IFrameBackdrop />, <IFrameContainer />, and <IFrame />) have both className and style object props, allowing the default styles to be overridden. At this moment, this is not recommended.

-> what is alternatively recommended from your side? The docs are missing details here..

How to Position the HostedForm?

I've got a working implementation, thanks for that!

I have a form with shipping address, billing address, payment info (via HostedForm), and then a Final Review button.

I input all the info and at the bottom I click the button generated by HostedForm.

That action opens the lightbox with the Authorize.net form, but it also renders that form near the top of the page and scrolls me up... forcing me to scroll all the way back down again to click Final Review.

How can we position the HostedForm?

Ideally, it wouldn't be a modal at all, but could be contained within a div I provide.

OR, how can I position it so that is pops up center screen at the current scroll location, and not at the top of the page?

Thanks!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.