Exploring the seamless integration of Worldpay with Angular 2

I am in the process of incorporating Worldpay into my angular2 application.

Utilizing the own form (own-form) approach, it is essential to include their script on the page:

<script src="https://cdn.worldpay.com/v1/worldpay.js"></script>
Adding specific attributes to certain inputs: data-worldpay and connecting the Worldpay.js logic to the form...

I have successfully completed the following steps:
1. Adding Worldpay.js to the page
2. Creating a payment form with the necessary attributes

How do I proceed with the next steps... I am currently facing this issue:

5. Link Worldpay.js to your form:

<script type="text/javascript">
var form = document.getElementById('paymentForm');

Worldpay.useOwnForm({
  'clientKey': 'your-test-client-key',
  'form': form,
  'reusable': false,
  'callback': function(status, response) {
    document.getElementById('paymentErrors').innerHTML = '';
    if (response.error) {             
      Worldpay.handleError(form, document.getElementById('paymentErrors'), response.error); 
    } else {
      var token = response.token;
      Worldpay.formBuilder(form, 'input', 'hidden', 'token', token);
      form.submit();
    }
  }
});
</script>

Why?
angular2 removes all <script tags from templates.
It might be possible to inject scripts into the page using a workaround in the ngAfterViewInit() method (similar to what was done for the first step)

ngAfterViewInit(): void {        
  let s = document.createElement("script");
  s.type = "text/javascript";
  s.src = "https://cdn.worldpay.com/v1/worldpay.js";
  this.worldPayScriptElement.nativeElement.appendChild(s);        
}

where this.worldPayScriptElement is a ViewChild of the div from the template:

<div #worldPayScriptElement hidden></div>

However, Due to their processing rules, Worldpay will replace sensitive data from my form with a field called CreditCardToken

As explained in the source: Ultimately, in Worldpay.formBuilder(), all sensitive card data is eliminated from the form, replaced with the token, and only then is the form submitted back to your server. source:

How do I proceed with integrating this further... It's quite challenging.
If they had an API that returns the CreditCardToken based on a GET/POST request, it would simplify the process, but I haven't found the appropriate method in the documentation yet...

I would greatly appreciate any suggestions.

Answer №1

After exploring different options, I have found a new approach to solving this issue.

To obtain the token, I utilized the Worldpay API.
API url:

https://api.worldpay.com/v1/tokens

The endpoint requires a POST request in the following format:

'{
    "reusable": true/false,
    "paymentMethod": {
        "name": "name",
        "expiryMonth": 2,
        "expiryYear": 2015,
        "issueNumber": 1,
        "startMonth": 2,
        "startYear": 2013,
        "cardNumber": "4444 3333 2222 1111",
        "type": "Card",
        "cvc": "123"
    },
    "clientKey": "T_C_client_key"
}'

The Header must include these options: "Content-type: application/json"

By doing this, there is no longer a need to include worldpay.js in the page.
Additionally, there is no requirement to include worldpay specific attributes in the payment form (like data-worldpay="")

Simply call the API, await the response, which will be structured as follows:

{
    "token": "UUID of token",
    "reusable": true/false,
    "paymentMethod": {
        "type" : "ObfuscatedCard",     
        "name": "name",
        "expiryMonth": 2,
        "expiryYear": 2015,
        "issueNumber": 1,
        "startMonth": 2,
        "startYear": 2013,
        "cardType": "VISA_CREDIT",
        "maskedCardNumber": "xxxx xxxx xxxx 1111",
        "cardSchemeType": "consumer",
        "cardSchemeName": "VISA CREDIT",
        "cardProductTypeDescNonContactless": "Visa Credit Personal",
        "cardProductTypeDescContactless": "CL Visa Credit Pers",
        "cardIssuer": "LLOYDS BANK PLC",
        "countryCode": "GBR",
        "cardClass": "credit",
        "prepaid": "false"
    }
}

Once you receive the response, use the response.token to proceed to the next step: payment.

Ensure that specific WorldPay attributes are included in the request (CreditCardToken, Enrolled)

Wondering how to call the worldpay API in angular2?

public getWorldpayToken(request: any): Observable<any>{
    let worldPayApiUrl = `https://api.worldpay.com/v1/tokens`;
    let body = JSON.stringify(request);
    let headers = new Headers({ 'Content-Type':'application/json;charset=UTF-8'});
    let options = new RequestOptions({ headers: headers });

    return this.http.post(worldPayApiUrl, body, options)
      .map(response => response.json())
      .catch(this.handleError);
}

For further information, refer to the documentation:

If you have any other questions or need more details, feel free to ask in the comments section.

Answer №2

If you're able to utilize the API, the accepted answer is excellent. However, if you're not PCI-DSS compliant, it's best to steer clear of the API.

When it comes to using a template form or your customized form in Angular 2+, the steps I've taken are outlined below:

  1. Add Worldpay.js in one of the following ways:

    • Include a local copy as a script in .angular-cli.json
    • Import a local copy in your component, for example: import '../../libs/worldpay';
    • Include a script tag in the main index.html to load it from the CDN:
      <script src="https://cdn.worldpay.com/v1/worldpay.js"></script>
  2. Assure Typescript that it's accessible in your component:

    declare var Worldpay: any;   
    
  3. Utilize a ViewChild to reference the form:

    @ViewChild('paymentForm') form;
    

    and in the HTML:

    <form #paymentForm>
    
  4. Create a callback function, such as:

    worldpayCallback(status, response) {        
      if (response.error) {
        this.handleError(response.error.object);
      } else {
        this.token = response.token;
      }
    }
    
  5. Initialize Worldpay in a suitable hook like:

    ngOnInit(): void {
      Worldpay.useTemplateForm({
        'clientKey': this.worldpayKey,
        'form': this.form.nativeElement,
        'callback': (status, response) => this.worldpayCallback(status, response)
      });
    }    
    

Once the form is submitted, you should now have access to the token.

Keep in mind that if you're using your own form, you'll need to adhere to PCI SAQ A-EP compliance, which involves a self-assessment process (involving extensive paperwork).

Answer №3

Here is a practical example using Angular 4/5. Make sure to insert your client api key into the Worldpay.component.ts file.

https://plnkr.co/edit/vz6YX68ykyBuHGiwGBVx

Sample Template:

<form #paymentForm *ngIf="!token">
    <div id="paymentSection"></div>
</form>
<h2 *ngIf="token">Token from WorldPay {{ token }}</h2>

Component:

import { Component, OnInit, ViewChild } from '@angular/core';
declare var Worldpay: any;

@Component({
  selector: 'app-worldpay',
  templateUrl: './worldpay.component.html'
})

export class WorldpayComponent implements OnInit {

  @ViewChild('paymentForm') form;
  readonly worldpayClientKey = 'INSERT_YOUR_KEY_HERE';
  token: string = null;

  constructor() { }

  ngOnInit() {
    this.loadScript('https://cdn.worldpay.com/v1/worldpay.js', this.init);
  }

  init = (): void => {
    Worldpay.useTemplateForm({
      'clientKey': this.worldpayClientKey,
      'form': this.form.nativeElement,
      'paymentSection': 'paymentSection',
      'display': 'inline',
      'type': 'card',
      'callback': this.worldpayCallback
    });
  }

  worldpayCallback = (status): void => {
    this.token = status.token;
  }

  private loadScript(url: string, callbackFunction: (any) = undefined) {
    const node = document.createElement('script');
    node.src = url;
    node.type = 'text/javascript';
    node.onload = callbackFunction;
    document.getElementsByTagName('body')[0].appendChild(node);
  }
}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Issue with mediaRecorder.ondataavailable in Angular 4 - need a solution

Currently, I am attempting to transmit real-time streaming data from an Angular 4 application to a NodeJS server. To achieve this, I have implemented the usage of socket.io and webRtc for streaming. constructor(private _chatService: ChatService) {} ngOnI ...

The battle of Any, Generic Type, and Unknown in Typescript

Is it advisable to replace the type any with Generic Types? I understand that using type any is generally discouraged as it removes type checking in TypeScript, making it unsafe. So what is a better alternative - using unknown or generic types? For examp ...

Exploring the world of Angular modules and lazy loading

Currently, I am working on an Ionic project which includes an IonicPage named Login. This page consists of several files such as login.html, login.module.ts, login.scss, and login.ts. Within the login.module.ts file, I have defined some providers for spec ...

Emphasize a modified attribute in *ngFor

I am currently working on a feature that highlights a value whenever it changes within an *ngFor loop to alert the user. I have made some progress in achieving this goal. https://stackblitz.com/edit/ngfor-zhywqy The ngFor directive is being used to displ ...

Can we create a class to represent a JSON object?

Can a JSON be modeled with a class in TypeScript (or Angular)? For example, I am using Firebase and have a node called /books structured like this: books -- 157sq561sqs1 -- author: 'Foo' -- title: 'Hello world' (Where 1 ...

Is there a way to extract the current view date information from fullcalendar using Angular?

When working with HTML code... <full-calendar defaultView="dayGridMonth" [plugins]="calendarPlugins" #calendar></fullcalendar> I am wondering how to extract the date information from the #calendar element. I attempted to consult the fullcal ...

Could someone show me how to modify the color of Material UI Label text in Angular?

Currently, I am developing my Angular university Project using the Mui library. In my logIn form, I have a Dark background and I would like to change the color of my Label Textfield to something different. Can anyone provide assistance? ...

Guide on how to import or merge JavaScript files depending on their references

As I work on my MVC 6 app, I am exploring a new approach to replacing the older js/css bundling & minification system. My goal is to generate a single javascript file that can be easily referenced in my HTML. However, this javascript file needs to be speci ...

Modifying the date format of the ag-Grid date filter

On my Angular 5.2.11 application, I utilize ag-grid to showcase a table. The date column is configured with the default date filter agDateColumnFilter as per the documentation. After enabling browserDatePicker: true, the Datepicker displays dates in the ...

One way in Angular to set a validator pattern regex that matches either one rule or the other, but

Trying to implement user range matching with angular validators, like shown in the image below: https://i.stack.imgur.com/zXHn3.png The goal is to validate against one of two range options using an angular pattern validator: 1-99 or 1,2,5,6,8, but not b ...

Having Trouble with Angular2: Nesting a Child Component?

Working with angular 2 has been a fun experience for me over the past few days. I've been attempting to insert a child selector into a parent template, but it seems more challenging than I expected. Every time I try, I encounter the following error: ...

What is the correct way to use forwardRef in a dynamic import in Next.js?

I've been trying to incorporate the forwardRef in my code, but I'm facing some difficulties. Can anyone help me out with this? I'm encountering the following errors: Property 'forwardedRef' does not exist on type '{}'. ...

The element's 'nativeElement' property cannot be accessed because it is undefined

I have a scenario where I have a parent component and a child component. I am trying to access the DOM element of the Child component from the Parent component, but I keep encountering an issue with the native element being undefined. export class ChildCom ...

How can I display the current index within an Angular component?

Currently, I am faced with a situation where an Angular component is being replaced for each element in an array: <app-doc-item *ngFor="let docFeatureEl of docFeatures; let i = index"; [documentFeature]="docFeatureEl" > ...

Can you explain the distinction between certain assignment assertion and ambient declaration?

When declaring that a field is definitely initialized within a class, what distinguishes the ! (exclamation point, definite assignment assertion) from the declare modifier? The subsequent code triggers an error in strict mode as TypeScript cannot confirm ...

The Redux Toolkit Slice is encountering an issue where it generates the incorrect type of "WritableDraft<AppApiError> when the extraReducer is

I defined my initial state as MednannyAppointments[] for data and AppApiError for error. However, when I hover over state.error or state.data in my extraReducer calls, the type is always WritableDraft. This behaviour is confusing to me. Even though I have ...

Unable to loop through using ngFor

I have a component that retrieves data from the back-end and groups it accordingly. Below is the code snippet: getRecruitmentAgencyClientPositions(): void { this._recruitmentAgencyClientsService.getRecruitmentAgencyClientPositions(this.recruitmentAge ...

Steps to activate zone-conscious bluebird assurances in Angular 8

In order to make all the promises in my Angular 8 project cancelable, I embarked on a quest to find the perfect library for the job. It was during this search that I stumbled upon bluebird.js, a promising candidate ;-) Following these guidelines on integr ...

Guide on sending a message to a specific channel using Discord.js version 13 with TypeScript

After recently diving into TypeScript and seeing that Discord.js has made the move to v13, I have encountered an issue with sending messages to a specific channel using a Channel ID. Below is the code snippet I am currently using: // Define Channel ID cons ...

Unexpectedly, a significant ngrx createEffect leads to an unusual error following an update, but the issue vanishes when certain code snippets like tap or filter are disabled

I have been in the process of upgrading a massive Angular 12 project to Angular 13 and have completed several steps. One significant change was the rewriting of Effects using a newer approach like createEffect(() => instead of @Effect However, during ...