Skip to content

Instantly share code, notes, and snippets.

@vladutilie
Created January 8, 2021 07:22
Show Gist options
  • Save vladutilie/49eac3391b10eb71b6dae501507314fa to your computer and use it in GitHub Desktop.
Save vladutilie/49eac3391b10eb71b6dae501507314fa to your computer and use it in GitHub Desktop.
BT API | Payment initiate @nestjs
import { HttpService, Injectable } from '@nestjs/common';
import { map } from 'rxjs/operators';
import { User } from '../user/user.entity';
import { BTTokenDTO, ExchangingDataDTO, MakePaymentDTO, PaymentInfoDTO, RegisterBTClientDTO } from './payment.dto';
import { v4 as uuidv4 } from 'uuid';
import sha256 from 'crypto-js/sha256';
import Base64 from 'crypto-js/enc-base64';
import qs from 'querystring';
import { PaymentRepository } from './payment.repository';
import { PaymentCredentials } from './payment-credentials.entity';
import { plainToClass } from 'class-transformer';
/* eslint-disable camelcase */
@Injectable()
export class BTPaymentService {
constructor(private httpService: HttpService, private paymentRepository: PaymentRepository) {}
async registerBToAuthClient(currentUser: User): Promise<RegisterBTClientDTO> {
const payload = {
redirect_uris: [process.env.BT_REDIRECT_URI],
company_name: 'TPP Corp.',
client_name: currentUser.name,
company_url: 'https://google.com',
contact_person: currentUser.name,
email_address: currentUser.email,
phone_number: currentUser.phone,
};
const options = {
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
};
return await this.httpService
.post(`${process.env.BT_PAYMENT_ENDPOINT}/oauth/register/TppOauthBT`, payload, options)
.pipe(map((response) => response.data))
.toPromise();
}
async create(user: User, register: RegisterBTClientDTO): Promise<PaymentCredentials> {
const credentials = await this.paymentRepository.findOne({ where: { user, bank: 'BT' } });
if (!credentials) {
const newCredentials = new PaymentCredentials();
newCredentials.user = user;
newCredentials.bank = 'BT';
newCredentials.clientId = register.client_id;
newCredentials.clientSecret = register.client_secret;
return await this.paymentRepository.save(newCredentials);
}
const newCredentials = plainToClass(PaymentCredentials, {
id: credentials.id,
clientId: register.client_id,
clientSecret: register.client_secret,
});
return await this.paymentRepository.save(newCredentials);
}
async makePayment(paymentInfo: PaymentInfoDTO): Promise<MakePaymentDTO> {
const payload = {
instructedAmount: {
currency: paymentInfo.currency,
amount: `${paymentInfo.amount}`,
},
// TODO: handle the below details.
creditorAccount: {
iban: 'RO98BTRLEURCRT0ABCDEFGHI',
},
creditorName: 'Creditor Name',
debtorId: 'J123456',
endToEndIdentification: 'TPP Reference',
remittanceInformationUnstructured: 'Merchant reference',
};
const options = {
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
// TODO: Location and IP address should be handled.
'PSU-Geo-Location': 'Romania',
'PSU-IP-Address': '22.33.44.55',
'X-Request-ID': uuidv4(),
},
};
return await this.httpService
.post(`${process.env.BT_PAYMENT_ENDPOINT}/bt-psd2/v2/payments/ron-payment`, payload, options)
.pipe(map((response) => response.data))
.toPromise();
}
async buildAuthUri(register: RegisterBTClientDTO, payment: MakePaymentDTO, codeVerifier: string): Promise<string> {
/*
https://apistorebt.ro/mga/sps/oauth/oauth20/authorize?
?response_type=code
&client_id=<OOKFE3nE54aW7GtCra1b>
&redirect_uri=<https://google.com>
&scope=PIS:<paymentId>
&state=<statetest>
&code_challenge=<Q8aVElfXiBwpn14GYiNZI_j2kee8OSHCt5DWTxbyBVs>
&code_challenge_method=S256
*/
const params = {
response_type: 'code',
client_id: register.client_id,
redirect_uri: process.env.BT_REDIRECT_URI,
scope: `PIS:${payment.paymentId}`,
state: 'statetest', // TODO: State code should be generated and verified after BT redirects back.
code_challenge: await this.computeCodeChallange(codeVerifier),
code_challenge_method: 'S256',
};
const authUri = Object.keys(params)
.map((key) => key + '=' + params[key])
.join('&');
return authUri;
}
async getToken(user: User, exchangingData: ExchangingDataDTO): Promise<BTTokenDTO> {
const credentials = await this.paymentRepository.findOne({ where: { user, bank: 'BT' } });
const payload = qs.stringify({
code: exchangingData.code,
grant_type: 'authorization_code',
redirect_uri: process.env.BT_REDIRECT_URI,
client_id: credentials.clientId,
client_secret: credentials.clientSecret,
code_verifier: process.env.BT_CODE_VERIFIER,
});
const options = {
headers: {
Accept: 'application/json',
'Content-Type': 'application/x-www-form-urlencoded',
},
};
return await this.httpService
.post(`${process.env.BT_PAYMENT_ENDPOINT}/oauth/token`, payload, options)
.pipe(map((response) => response.data))
.toPromise();
}
async updateToken(user: User, tokenData: BTTokenDTO): Promise<PaymentCredentials> {
const credentials = await this.paymentRepository.findOne({ where: { user, bank: 'BT' } });
credentials.accessToken = tokenData.access_token;
credentials.refreshToken = tokenData.refresh_token;
const expiresAt = new Date();
expiresAt.setSeconds(parseInt(expiresAt.getSeconds() + tokenData.expires_in));
credentials.expiresAt = expiresAt;
return await this.paymentRepository.save(credentials);
}
async computeCodeChallange(codeVerifier: string): Promise<string> {
return await sha256(codeVerifier).toString(Base64).replace(/=/g, '').replace(/\+/g, '-').replace(/\//g, '_');
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment