Integrar diferentes gateways de pagamento pode parecer uma tarefa desafiadora, mas imagine a tranquilidade de ter uma solução que torna esse processo simples e eficiente. Com Design Pattern Adapter, você terá o controle total sobre as integrações, facilitando a manutenção e expansão do seu sistema.
Agora, visualize o poder de dominar uma habilidade que não apenas economiza tempo, mas também aumenta a qualidade do seu código. Neste artigo, vamos revelar como você pode se destacar ao integrar um gateway de pagamento usando Node.js e Fastify, uma tecnologia que tem conquistado desenvolvedores em todo o mundo.
Se você está comprometido em levar suas habilidades ao próximo nível, este conteúdo é para você. Vamos explorar juntos a criação de cobranças PIX com a API da Woovi, além de outras funcionalidades que farão você se destacar no mercado.
Abordaremos a integração de um gateway de pagamento utilizando Node.js e Fastify. Você aprenderá a gerar cobranças via PIX usando a API da Woovi, além de outras funcionalidades.
Este artigo faz parte das aulas do CrazyStack Node.js, onde desenvolvemos do zero uma API REST utilizando Node.js e Fastify. Você pode acompanhar o início do tutorial através dos vídeos aqui e aqui.
Vamos estruturar o projeto de forma modular, onde cada gateway de pagamento terá sua própria implementação, mas todos compartilharão um contrato comum. Utilizaremos TypeScript para garantir a tipagem estática e a segurança do código.
O primeiro passo é definir um contrato que todos os gateways de pagamento devem implementar. Isso assegura que todos os gateways tenham as mesmas funções com as mesmas assinaturas, garantindo consistência.
// src/contracts/PaymentGateway.ts export abstract class PaymentGateway { abstract createCharge(data: any): Promise; abstract deleteCharge(id: string): Promise ; abstract getCharge(id: string): Promise ; abstract createSubscription(data: any): Promise ; abstract getSubscription(id: string): Promise ; abstract createCustomer(data: any): Promise ; abstract getCustomer(id: string): Promise ; abstract getChargeByCustomer(data: any): Promise ; }
A implementação do adapter para o Woovi usa a biblioteca axios para realizar as chamadas HTTP.
// 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{ 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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); };
Para o Stripe, utilizamos o SDK oficial stripe.
// 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{ 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 { 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 { try { const subscription = await this.stripe.subscriptions.retrieve(id); return { subscription }; } catch (e: any) { return e?.response?.data; } } async deleteCharge(id: string): Promise { 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 { try { const charge = await this.stripe.paymentIntents.retrieve(id); return { charge }; } catch (e: any) { return e?.response?.data; } } async createCharge(data: any): Promise { 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 { 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 { 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); };
A documentação da Pagar.me detalha como criar um cliente utilizando a API deles. Através de uma requisição POST para o endpoint /customers, é possível cadastrar um novo cliente na plataforma. Importante notar que o campo email é único: se um cliente com o mesmo email já existir, os dados serão atualizados em vez de criar um novo registro. Além disso, clientes com passaporte só podem transacionar com endereços internacionais válidos.
Agora, explicando o PagarmeAdapter com base nessa documentação:
O PagarmeAdapter é uma implementação de um adaptador que permite interagir com a API da Pagar.me para criar e gerenciar clientes, cobranças, e assinaturas. Ele utiliza a biblioteca axios para realizar chamadas HTTP à API da Pagar.me.
Essa função envia uma requisição POST para o endpoint /customers da Pagar.me, passando os dados do cliente no corpo da requisição. O axios lida com a autenticação utilizando o token de API (Bearer ${this.apiKey}) e retorna os dados do cliente criado ou atualizado.
Exemplo de uso:
async createCustomer(data: any): Promise{ 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; } }
Esta função é essencial para cadastrar ou atualizar clientes na Pagar.me diretamente de sua aplicação Node.js usando o padrão Adapter, garantindo a flexibilidade e modularidade do sistema.
Para mais detalhes sobre a criação de clientes na Pagar.me, consulte a documentação oficial aqui.
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.
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:
Exemplo de uso:
async getCustomer(id: string): Promise{ 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; } }
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.
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.
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:
Exemplo de uso:
async getCustomer(id: string): Promise{ 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; } }
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.
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{ 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 { 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 { 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 { 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 { 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); };
{ "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": "[email protected]", "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 } ] }
{ "amount": 2990 }
{ "amount": 2990 }
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{ 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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); };
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:
Este curso é um treinamento prático e intensivo em formato de bootcamp, focado em desenvolvedores plenos e seniores que desejam evoluir a forma como escrevem código. Você aprenderá conceitos avançados como Design Patterns, Clean Architecture, TDD e DDD, aplicados em projetos reais com Node.js e Fastify.
Saiba mais e inscreva-se!
Descargo de responsabilidad: Todos los recursos proporcionados provienen en parte de Internet. Si existe alguna infracción de sus derechos de autor u otros derechos e intereses, explique los motivos detallados y proporcione pruebas de los derechos de autor o derechos e intereses y luego envíelos al correo electrónico: [email protected]. Lo manejaremos por usted lo antes posible.
Copyright© 2022 湘ICP备2022001581号-3