Skip to content

Instantly share code, notes, and snippets.

@vladutilie
Created January 8, 2021 07:24
Show Gist options
  • Save vladutilie/715ea6809dd05947124447807f08b597 to your computer and use it in GitHub Desktop.
Save vladutilie/715ea6809dd05947124447807f08b597 to your computer and use it in GitHub Desktop.
ING API | Get token and payment initiate @nestjs
import { HttpService, Injectable } from '@nestjs/common';
import { PaymentRepository } from './payment.repository';
import { map } from 'rxjs/operators';
import fs from 'fs';
import crypto from 'crypto';
import https from 'https';
import { User } from '../user/user.entity';
import { LessThan, MoreThan } from 'typeorm';
import { INGTokenDTO } from './payment.dto';
import { PaymentCredentials } from './payment-credentials.entity';
import { v4 as uuidv4 } from 'uuid';
@Injectable()
export class INGPaymentService {
constructor(private httpService: HttpService, private paymentRepository: PaymentRepository) {}
async initiatePayment(token: PaymentCredentials) {
const keyId = token.clientId;
const requestedId = uuidv4();
const reqDate = new Date().toUTCString();
const payload = JSON.stringify({
instructedAmount: { amount: '1', currency: 'EUR' },
creditorAccount: { iban: 'AT861921125678901234' },
creditorName: 'Laura Musterfraus',
});
const payloadDigest = crypto.createHash('sha256').update(payload).digest('base64');
const digest = `SHA-256=${payloadDigest}`;
// KEEP signingString without indentation on the lines with "date" and "digest" values!
const signingString = `(request-target): post /v1/payments/sepa-credit-transfers
date: ${reqDate}
digest: ${digest}
x-request-id: ${requestedId}`;
const privateKey = fs.readFileSync('./storage/ing/eidas_client_signing.key', 'utf-8');
const signatureHash = crypto.createSign('sha256').update(signingString).sign(privateKey).toString('base64');
const signature = `keyId="${keyId}",algorithm="rsa-sha256",headers="(request-target) date digest x-request-id",signature="${signatureHash}"`;
const httpsAgent = new https.Agent({
rejectUnauthorized: false, // This will disable client verification
cert: fs.readFileSync('./storage/ing/eidas_client_tls.cer'),
key: fs.readFileSync('./storage/ing/eidas_client_tls.key'),
});
const options = {
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
Digest: digest,
Date: reqDate,
// TODO: Change TPP-Redirect-URI with process.env.ING_REDIRECT_URI when on production.
'TPP-Redirect-URI': 'https://example.com/redirect',
'PSU-IP-Address': '37.44.220.0',
'X-Request-ID': requestedId,
Authorization: `Bearer ${token.accessToken}`,
Signature: signature,
},
httpsAgent,
};
return await this.httpService
.post(`${process.env.ING_PAYMENT_ENDPOINT}/v1/payments/sepa-credit-transfers`, payload, options)
.pipe(map((response) => response.data))
.toPromise();
}
async getToken(user: User): Promise<PaymentCredentials> {
const now = new Date();
const token = await this.paymentRepository.findOne({
where: { user, bank: 'ING', expiresAt: MoreThan(now) },
});
if (!token) {
// Deletes expired access_token.
await this.paymentRepository.delete({ user, bank: 'ING', expiresAt: LessThan(now) });
const newToken = await this.authRequest();
const credentials = new PaymentCredentials();
credentials.user = user;
credentials.bank = 'ING';
credentials.clientId = newToken.client_id;
credentials.accessToken = newToken.access_token;
now.setSeconds(now.getSeconds() + newToken.expires_in);
credentials.expiresAt = now;
await this.paymentRepository.save(credentials);
return credentials;
}
return token;
}
async authRequest(): Promise<INGTokenDTO> {
const keyId = `SN=${process.env.ING_CERT_SN}`;
const payload = 'grant_type=client_credentials';
const payloadDigest = crypto.createHash('sha256').update(payload).digest('base64');
const digest = `SHA-256=${payloadDigest}`;
const reqDate = new Date().toUTCString();
// KEEP signingString without indentation on the lines with "date" and "digest" values!
const signingString = `(request-target): post /oauth2/token
date: ${reqDate}
digest: ${digest}`;
const privateKey = fs.readFileSync('./storage/ing/eidas_client_signing.key', 'utf-8');
const certificate = fs.readFileSync('./storage/ing/eidas_client_signing.cer', 'utf-8');
const signature = crypto.createSign('sha256').update(signingString).sign(privateKey);
const signatureBase64 = signature.toString('base64');
const authorization = `Signature keyId="${keyId}",algorithm="rsa-sha256",headers="(request-target) date digest",signature="${signatureBase64}"`;
const httpsAgent = new https.Agent({
rejectUnauthorized: false, // This will disable client verification
cert: fs.readFileSync('./storage/ing/eidas_client_tls.cer'),
key: fs.readFileSync('./storage/ing/eidas_client_tls.key'),
});
const options = {
headers: {
Accept: 'application/json',
'Content-Type': 'application/x-www-form-urlencoded',
Digest: digest,
Date: reqDate,
'TPP-Signature-Certificate': certificate.replace(/[\r\n]+/gm, ''),
authorization,
},
httpsAgent,
};
return await this.httpService
.post(`${process.env.ING_PAYMENT_ENDPOINT}/oauth2/token`, payload, options)
.pipe(map((response) => response.data))
.toPromise();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment