Integration of Payment Gateways Using the Adapter Pattern in Node.js and Fastify
Integrating different payment gateways may seem like a challenging task, but imagine the peace of mind of having a solution that makes this process simple and efficient. With Design Pattern Adapter, you will have full control over integrations, facilitating the maintenance and expansion of your system.
Now, visualize the power of mastering a skill that not only saves you time but also increases the quality of your code. In this article, we will reveal how you can stand out when integrating a payment gateway using Node.js and Fastify, a technology that has won over developers around the world.
If you are committed to taking your skills to the next level, this content is for you. Let's explore together the creation of PIX charges with the Woovi API, as well as other features that will make you stand out in the market.
We will cover the integration of a payment gateway using Node.js and Fastify. You will learn how to generate charges via PIX using the Woovi API, in addition to other features.
This article is part of the CrazyStack Node.js classes, where we developed a REST API from scratch using Node.js and Fastify. You can follow the beginning of the tutorial through the videos here and here.
Project Structure
We will structure the project in a modular way, where each payment gateway will have its own implementation, but everyone will share a common contract. We will use TypeScript to ensure static typing and code security.
Directories and Files
-
src/
- contracts/
- PaymentGateway.ts (Contract common to all gateways)
- adapters/
- WooviAdapter.ts (Woovi gateway implementation)
- StripeAdapter.ts (Stripe gateway implementation)
- PagarmeAdapter.ts (Implementation of the Pagar.me gateway)
- index.ts (adapters entry point)
- config/
- env.ts (Environment Settings)
Payment Gateway Agreement
The first step is to define a contract that all payment gateways must implement. This ensures that all gateways have the same functions with the same signatures, ensuring consistency.
// src/contracts/PaymentGateway.ts export abstract class PaymentGateway { abstract createCharge(data: any): Promise<any>; abstract deleteCharge(id: string): Promise<any>; abstract getCharge(id: string): Promise<any>; abstract createSubscription(data: any): Promise<any>; abstract getSubscription(id: string): Promise<any>; abstract createCustomer(data: any): Promise<any>; abstract getCustomer(id: string): Promise<any>; abstract getChargeByCustomer(data: any): Promise<any>; } </any></any></any></any></any></any></any></any>
Adapters for Payment Gateways
Woovi Payment Gateway
The adapter implementation for Woovi uses the axios library to make HTTP calls.
// src/adapters/WooviAdapter.ts import axios from "axios"; import { PaymentGateway } from "../contracts"; import { env } from "../config"; export class WooviPaymentGateway extends PaymentGateway { private apiKey: string; constructor(paymentKey: string) { super(); this.apiKey = paymentKey; } async deleteCharge(id: string): Promise<any> { try { const response = await axios.delete( `https://api.openpix.com.br/api/v1/charge/${id}`, { headers: { Authorization: this.apiKey }, } ); return response?.data; } catch (e: any) { return e?.response?.data; } } async getCharge(id: string): Promise<any> { try { const response = await axios.get( `https://api.openpix.com.br/api/v1/charge/${id}`, { headers: { Authorization: this.apiKey, "content-type": "application/json" }, } ); return response?.data; } catch (e: any) { return e?.response?.data; } } async createCharge(data: any): Promise<any> { const { correlationID, value, comment } = data; try { const { data } = await axios.post( "https://api.openpix.com.br/api/v1/charge?return_existing=true", { correlationID, value, comment }, { headers: { Authorization: this.apiKey, "content-type": "application/json" }, } ); return data; } catch (e: any) { return e?.response?.data; } } async createSubscription(body: any): Promise<any> { try { const { data } = await axios.post( "https://api.openpix.com.br/api/v1/subscriptions", body, { headers: { Authorization: this.apiKey, "content-type": "application/json" }, } ); return data; } catch (e: any) { return e?.response?.data; } } async getSubscription(id: string): Promise<any> { try { const response = await axios.get( `https://api.openpix.com.br/api/v1/subscriptions/${id}`, { headers: { Authorization: this.apiKey, "content-type": "application/json" }, } ); return response?.data; } catch (e: any) { return e?.response?.data; } } async createCustomer(body: any): Promise<any> { try { const { data } = await axios.post( "https://api.openpix.com.br/api/v1/customer", body, { headers: { Authorization: this.apiKey, "content-type": "application/json" }, } ); return data; } catch (e: any) { return e?.response?.data; } } async getCustomer(id: string): Promise<any> { try { const response = await axios.get( `https://api.openpix.com.br/api/v1/customer/${id}`, { headers: { Authorization: this.apiKey, "content-type": "application/json" }, } ); return response?.data; } catch (e: any) { return e?.response?.data; } } async getChargeByCustomer(correlationID: string): Promise<any> { try { const response = await axios.get( `https://api.openpix.com.br/api/v1/charge?customer=${correlationID}&status=ACTIVE`, { headers: { Authorization: this.apiKey, "content-type": "application/json" }, } ); return response?.data; } catch (e: any) { return e?.response?.data; } } } export const makeWooviAdapter = () => { return new WooviPaymentGateway(env.wooviKey); }; </any></any></any></any></any></any></any></any>
Stripe Payment Gateway
For Stripe, we use the official stripe SDK.
// src/adapters/StripeAdapter.ts import { PaymentGateway } from "../contracts"; import { env } from "../config"; import Stripe from "stripe"; export class StripePaymentGateway extends PaymentGateway { private stripe: Stripe; constructor(paymentKey: string) { super(); this.stripe = new Stripe(paymentKey, { apiVersion: "2023-10-16", typescript: true, }); } async createPrice(amount: number): Promise<any> { try { const price = await this.stripe.prices.create({ currency: "brl", unit_amount: amount, recurring: { interval: "month" }, product_data: { name: "Gold Plan" }, }); return { price }; } catch (e: any) { return e?.response?.data; } } async createSubscription(data: any): Promise<any> { try { const subscription = await this.stripe.subscriptions.create({ customer: data?.customer?.id ?? data?.customer?.correlationID, items: [{ price: data?.priceId }], }); return { subscription }; } catch (e: any) { return e?.response?.data; } } async getSubscription(id: string): Promise<any> { try { const subscription = await this.stripe.subscriptions.retrieve(id); return { subscription }; } catch (e: any) { return e?.response?.data; } } async deleteCharge(id: string): Promise<any> { try { const charge = await this.stripe.paymentIntents.update(id, { metadata: { status: "canceled" }, }); return { charge, status: "OK" }; } catch (e: any) { return e?.response?.data; } } async getCharge(id: string): Promise<any> { try { const charge = await this.stripe.paymentIntents.retrieve(id); return { charge }; } catch (e: any) { return e?.response?.data; } } async createCharge(data: any): Promise<any> { try { const charge = await this.stripe.paymentIntents.create({ amount: Number(data?.value), currency: "brl", metadata: { metadata: JSON.stringify(data) }, automatic_payment_methods: { enabled: true }, }); return { charge }; } catch (e: any) { return e?.response?.data; } } async createCustomer(data: any): Promise<any> { const { email, description } = data; try { const customer: Stripe.Customer = await this.stripe.customers.create({ description, email , }); return { customer }; } catch (e: any) { return e?.response?.data; } } async getCustomer(id: string): Promise<any> { try { const customer = await this.stripe.customers.retrieve(id); return { customer }; } catch (e: any) { return e?.response?.data; } } } export const makeStripeAdapter = () => { return new StripePaymentGateway(env.stripeKeySecret); }; </any></any></any></any></any></any></any></any>
Pagar.me Payment Gateway
Pagar.me's documentation details how to create a client using their API. Through a POST request to the /customers endpoint, it is possible to register a new customer on the platform. It is important to note that the email field is unique: if a customer with the same email already exists, the data will be updated instead of creating a new record. Additionally, customers with a passport can only transact with valid international addresses.
Now, explaining PagarmeAdapter based on this documentation:
Explaining PagarmeAdapter
PagarmeAdapter is an implementation of an adapter that allows you to interact with the Pagar.me API to create and manage customers, charges, and subscriptions. It uses the axios library to make HTTP calls to the Pagar.me API.
createCustomer function
This function sends a POST request to the Pagar.me /customers endpoint, passing the customer data in the body of the request. axios handles authentication using the API token (Bearer ${this.apiKey}) and returns the created or updated client data.
Example of use:
async createCustomer(data: any): Promise<any> { try { const response = await axios.post( "https://api.pagar.me/1/customers", data, { headers: { Authorization: `Bearer ${this.apiKey}` }, } ); return response?.data; } catch (e: any) { return e?.response?.data; } } </any>
This function is essential for registering or updating customers on Pagar.me directly from your Node.js application using the Adapter pattern, ensuring the flexibility and modularity of the system.
For more details on creating customers on Pagar.me, see the official documentation here.
Get customer
The Pagar.me documentation explains how to obtain details of an already registered customer using the API. The specific endpoint for this is GET https://api.pagar.me/core/v5/customers/{customer_id}, where {customer_id} is the identifier of the customer you want to query.
Explicação do PagarmeAdapter - Função getCustomer
A função getCustomer dentro do PagarmeAdapter realiza exatamente essa operação. Ela faz uma requisição GET para o endpoint da Pagar.me, utilizando o customer_id fornecido. Aqui está como funciona:
- Autenticação: A função utiliza o token de API (Bearer ${this.apiKey}) para autenticar a requisição.
- Requisição: Faz a chamada GET para o endpoint da Pagar.me, buscando os detalhes do cliente correspondente ao customer_id.
- Resposta: Retorna os dados do cliente se a requisição for bem-sucedida ou a resposta de erro em caso de falha.
Exemplo de uso:
async getCustomer(id: string): Promise<any> { try { const response = await axios.get( `https://api.pagar.me/1/customers/${id}`, { headers: { Authorization: `Bearer ${this.apiKey}` }, } ); return response?.data; } catch (e: any) { return e?.response?.data; } } </any>
Essa função permite que você obtenha informações detalhadas sobre um cliente específico, diretamente da API da Pagar.me, integrando facilmente essa funcionalidade ao seu sistema Node.js. Para mais detalhes, você pode consultar a documentação oficial aqui.
Criando transactions
A documentação da Pagar.me explica como obter detalhes de um cliente já cadastrado usando a API. O endpoint específico para isso é o GET https://api.pagar.me/core/v5/customers/{customer_id}, onde {customer_id} é o identificador do cliente que você deseja consultar.
Explicação do PagarmeAdapter - Função getCustomer
A função getCustomer dentro do PagarmeAdapter realiza exatamente essa operação. Ela faz uma requisição GET para o endpoint da Pagar.me, utilizando o customer_id fornecido. Aqui está como funciona:
- Autenticação: A função utiliza o token de API (Bearer ${this.apiKey}) para autenticar a requisição.
- Requisição: Faz a chamada GET para o endpoint da Pagar.me, buscando os detalhes do cliente correspondente ao customer_id.
- Resposta: Retorna os dados do cliente se a requisição for bem-sucedida ou a resposta de erro em caso de falha.
Exemplo de uso:
async getCustomer(id: string): Promise<any> { try { const response = await axios.get( `https://api.pagar.me/1/customers/${id}`, { headers: { Authorization: `Bearer ${this.apiKey}` }, } ); return response?.data; } catch (e: any) { return e?.response?.data; } } </any>
Essa função permite que você obtenha informações detalhadas sobre um cliente específico, diretamente da API da Pagar.me, integrando facilmente essa funcionalidade ao seu sistema Node.js. Para mais detalhes, você pode consultar a documentação oficial aqui.
Vamos expandir o PagarmeAdapter para incluir métodos específicos para lidar com transações de cartão de crédito, seguindo a documentação da API Pagar.me. Também fornecerei exemplos de payloads de teste que você pode usar para verificar cada método.
Métodos do PagarmeAdapter para Cartão de Crédito
Aqui está a implementação dos métodos do PagarmeAdapter:
import axios from "axios"; import { PaymentGateway } from "../contracts"; import { env } from "../config"; export class PagarmePaymentGateway extends PaymentGateway { private apiKey: string; constructor(paymentKey: string) { super(); this.apiKey = paymentKey; } async createCharge(data: any): Promise<any> { try { const response = await axios.post( "https://api.pagar.me/1/transactions", data, { headers: { Authorization: `Bearer ${this.apiKey}` }, } ); return response?.data; } catch (e: any) { return e?.response?.data; } } async deleteCharge(id: string): Promise<any> { try { const response = await axios.delete( `https://api.pagar.me/1/transactions/${id}`, { headers: { Authorization: `Bearer ${this.apiKey}` }, } ); return response?.data; } catch (e: any) { return e?.response?.data; } } async getCharge(id: string): Promise<any> { try { const response = await axios.get( `https://api.pagar.me/1/transactions/${id}`, { headers: { Authorization: `Bearer ${this.apiKey}` }, } ); return response?.data; } catch (e: any) { return e?.response?.data; } } async captureCharge(id: string, amount: number): Promise<any> { try { const response = await axios.post( `https://api.pagar.me/1/transactions/${id}/capture`, { amount }, { headers: { Authorization: `Bearer ${this.apiKey}` }, } ); return response?.data; } catch (e: any) { return e?.response?.data; } } async refundCharge(id: string, amount: number): Promise<any> { try { const response = await axios.post( `https://api.pagar.me/1/transactions/${id}/refund`, { amount }, { headers: { Authorization: `Bearer ${this.apiKey}` }, } ); return response?.data; } catch (e: any) { return e?.response?.data; } } } export const makePagarmeAdapter = () => { return new PagarmePaymentGateway(env.pagarmeKey); }; </any></any></any></any></any>
Exemplos de Payloads de Teste
- Criação de Transação com Cartão de Crédito (Auth & Capture)
{ "amount": 2990, "payment_method": "credit_card", "card_number": "4000000000000010", "card_cvv": "123", "card_expiration_date": "1225", "card_holder_name": "Tony Stark", "customer": { "external_id": "#3311", "name": "Tony Stark", "type": "individual", "country": "br", "email": "tonystark@avengers.com", "documents": [ { "type": "cpf", "number": "12345678909" } ], "phone_numbers": ["+5511999998888"], "birthday": "1967-03-01" }, "billing": { "name": "Tony Stark", "address": { "country": "br", "state": "sp", "city": "Sao Paulo", "neighborhood": "Bela Vista", "street": "Avenida Paulista", "street_number": "1000", "zipcode": "01310000" } }, "items": [ { "id": "r123", "title": "Chaveiro do Tesseract", "unit_price": 2990, "quantity": 1, "tangible": true } ] }
- Captura de Transação Pré-autorizada
{ "amount": 2990 }
- Reembolso de Transação
{ "amount": 2990 }
Explicação
- createCharge: Cria uma nova transação de cartão de crédito.
- deleteCharge: Cancela uma transação existente.
- getCharge: Obtém os detalhes de uma transação específica.
- captureCharge: Captura uma transação que foi previamente autorizada.
- refundCharge: Realiza o estorno de uma transação.
Esses métodos cobrem as principais operações que você pode realizar com transações de cartão de crédito utilizando a API Pagar.me. Os payloads fornecidos são exemplos básicos que você pode utilizar para testar essas funcionalidades.
Código completo
// src/adapters/PagarmeAdapter.ts import axios from "axios"; import { PaymentGateway } from "../contracts"; import { env } from "../config"; export class PagarmePaymentGateway extends PaymentGateway { private apiKey: string; constructor(paymentKey: string) { super(); this.apiKey = paymentKey; } async createCharge(data: any): Promise<any> { try { const response = await axios.post( "https://api.pagar.me/1/transactions", data, { headers: { Authorization: `Bearer ${this.apiKey}` }, } ); return response?.data; } catch (e: any) { return e?.response?.data; } } async deleteCharge(id: string): Promise<any> { try { const response = await axios.delete( `https://api.pagar.me/1/transactions/${id}`, { headers: { Authorization: `Bearer ${this.apiKey}` }, } ); return response?.data; } catch (e: any) { return e?.response?.data; } } async getCharge(id: string): Promise<any> { try { const response = await axios.get( `https://api.pagar.me/1/transactions/${id}`, { headers: { Authorization: `Bearer ${this.apiKey}` }, } ); return response?.data; } catch (e: any) { return e?.response?.data; } } async createSubscription(data: any): Promise<any> { try { const response = await axios.post( "https://api.pagar.me/1/subscriptions", data, { headers: { Authorization: `Bearer ${this.apiKey}` }, } ); return response?.data; } catch (e: any) { return e?.response?.data; } } async getSubscription(id: string): Promise<any> { try { const response = await axios.get( `https://api.pagar.me/1/subscriptions/${id}`, { headers: { Authorization: `Bearer ${this.apiKey}` }, } ); return response?.data; } catch (e: any) { return e?.response?.data; } } async createCustomer(data: any): Promise<any> { try { const response = await axios.post( "https://api.pagar.me/1/customers", data, { headers: { Authorization: `Bearer ${this.apiKey}` }, } ); return response?.data; } catch (e: any) { return e?.response?.data; } } async getCustomer(id: string): Promise<any> { try { const response = await axios.get( `https://api.pagar.me/1/customers/${id}`, { headers: { Authorization: `Bearer ${this.apiKey}` }, } ); return response?.data; } catch (e: any) { return e?.response?.data; } } async getChargeByCustomer(correlationID: string): Promise<any> { try { const response = await axios.get( `https://api.pagar.me/1/transactions?customer=${correlationID}`, { headers: { Authorization: `Bearer ${this.apiKey}` }, } ); return response?.data; } catch (e: any) { return e?.response?.data; } } } export const makePagarmeAdapter = () => { return new PagarmePaymentGateway(env.pagarmeKey); }; </any></any></any></any></any></any></any></any>
Conclusão
Implementar gateways de pagamento utilizando o padrão Adapter em TypeScript facilita a integração e a manutenção do código. Ao seguir essa abordagem, você garante flexibilidade e modularidade no seu sistema, podendo adicionar ou substituir gateways com facilidade.
Para uma compreensão mais detalhada e prática sobre como implementar um gateway de pagamento com Node.js e Fastify, assista ao nosso vídeo tutorial completo na Aula 99 do CrazyStack Node.js. Não perca essa oportunidade de aprofundar seu conhecimento e dominar as melhores práticas de desenvolvimento de sistemas de pagamento.
? Links Importantes:
- CrazyStack TypeScript Course: crazystack.com.br
- Repository on GitHub: CrazyStackNodeJs
This course is practical and intensive training in a bootcamp format, focused on full-time and senior developers who want to evolve the way they write code. You will learn advanced concepts such as Design Patterns, Clean Architecture, TDD and DDD, applied in real projects with Node.js and Fastify.
Learn more and sign up!
The above is the detailed content of Payment gateway in general doesn't have to be complicated. For more information, please follow other related articles on the PHP Chinese website!

The latest trends in JavaScript include the rise of TypeScript, the popularity of modern frameworks and libraries, and the application of WebAssembly. Future prospects cover more powerful type systems, the development of server-side JavaScript, the expansion of artificial intelligence and machine learning, and the potential of IoT and edge computing.

JavaScript is the cornerstone of modern web development, and its main functions include event-driven programming, dynamic content generation and asynchronous programming. 1) Event-driven programming allows web pages to change dynamically according to user operations. 2) Dynamic content generation allows page content to be adjusted according to conditions. 3) Asynchronous programming ensures that the user interface is not blocked. JavaScript is widely used in web interaction, single-page application and server-side development, greatly improving the flexibility of user experience and cross-platform development.

Python is more suitable for data science and machine learning, while JavaScript is more suitable for front-end and full-stack development. 1. Python is known for its concise syntax and rich library ecosystem, and is suitable for data analysis and web development. 2. JavaScript is the core of front-end development. Node.js supports server-side programming and is suitable for full-stack development.

JavaScript does not require installation because it is already built into modern browsers. You just need a text editor and a browser to get started. 1) In the browser environment, run it by embedding the HTML file through tags. 2) In the Node.js environment, after downloading and installing Node.js, run the JavaScript file through the command line.

How to send task notifications in Quartz In advance When using the Quartz timer to schedule a task, the execution time of the task is set by the cron expression. Now...

How to obtain the parameters of functions on prototype chains in JavaScript In JavaScript programming, understanding and manipulating function parameters on prototype chains is a common and important task...

Analysis of the reason why the dynamic style displacement failure of using Vue.js in the WeChat applet web-view is using Vue.js...

How to make concurrent GET requests for multiple links and judge in sequence to return results? In Tampermonkey scripts, we often need to use multiple chains...


Hot AI Tools

Undresser.AI Undress
AI-powered app for creating realistic nude photos

AI Clothes Remover
Online AI tool for removing clothes from photos.

Undress AI Tool
Undress images for free

Clothoff.io
AI clothes remover

AI Hentai Generator
Generate AI Hentai for free.

Hot Article

Hot Tools

mPDF
mPDF is a PHP library that can generate PDF files from UTF-8 encoded HTML. The original author, Ian Back, wrote mPDF to output PDF files "on the fly" from his website and handle different languages. It is slower than original scripts like HTML2FPDF and produces larger files when using Unicode fonts, but supports CSS styles etc. and has a lot of enhancements. Supports almost all languages, including RTL (Arabic and Hebrew) and CJK (Chinese, Japanese and Korean). Supports nested block-level elements (such as P, DIV),

SublimeText3 Linux new version
SublimeText3 Linux latest version

MantisBT
Mantis is an easy-to-deploy web-based defect tracking tool designed to aid in product defect tracking. It requires PHP, MySQL and a web server. Check out our demo and hosting services.

SublimeText3 Chinese version
Chinese version, very easy to use

Safe Exam Browser
Safe Exam Browser is a secure browser environment for taking online exams securely. This software turns any computer into a secure workstation. It controls access to any utility and prevents students from using unauthorized resources.