”工欲善其事,必先利其器。“—孔子《论语.录灵公》
首页 > 编程 > 支付网关一般不必太复杂

支付网关一般不必太复杂

发布于2024-09-01
浏览:375

Gateway de pagamento de forma genérica não precisa ser complicado

Integração de Gateways de Pagamento Usando o Padrão Adapter em Node.js e Fastify

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.

Estrutura do Projeto

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.

Diretórios e Arquivos

  • src/
    • contracts/
    • PaymentGateway.ts (Contrato comum a todos os gateways)
    • adapters/
    • WooviAdapter.ts (Implementação do gateway Woovi)
    • StripeAdapter.ts (Implementação do gateway Stripe)
    • PagarmeAdapter.ts (Implementação do gateway Pagar.me)
    • index.ts (Ponto de entrada dos adapters)
    • config/
    • env.ts (Configurações de ambiente)

Contrato de Gateway de Pagamento

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;
}

Adapters para Gateways de Pagamento

Woovi Payment Gateway

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);
};

Stripe Payment Gateway

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);
};

Pagar.me Payment Gateway

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:

Explicando o PagarmeAdapter

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.

Função createCustomer

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.

Obter cliente

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:

  1. Autenticação: A função utiliza o token de API (Bearer ${this.apiKey}) para autenticar a requisição.
  2. Requisição: Faz a chamada GET para o endpoint da Pagar.me, buscando os detalhes do cliente correspondente ao customer_id.
  3. 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 {
    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.

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:

  1. Autenticação: A função utiliza o token de API (Bearer ${this.apiKey}) para autenticar a requisição.
  2. Requisição: Faz a chamada GET para o endpoint da Pagar.me, buscando os detalhes do cliente correspondente ao customer_id.
  3. 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 {
    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.

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 {
    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);
};

Exemplos de Payloads de Teste

  1. 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": "[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
        }
    ]
}
  1. Captura de Transação Pré-autorizada
{
    "amount": 2990
}
  1. 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 {
    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);
};

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:

  • Curso CrazyStack TypeScript: crazystack.com.br
  • Repositório no GitHub: CrazyStackNodeJs

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!

版本声明 本文转载于:https://dev.to/devdoido/gateway-de-pagamento-de-forma-generica-nao-precisa-ser-complicado-4dl2?1如有侵犯,请联系[email protected]删除
最新教程 更多>
  • React 中的 UseEffect
    React 中的 UseEffect
    欢迎来到 React Hooks 的世界!今天,我们将深入探讨最流行的挂钩之一:useEffect。别担心,我们会让它变得有趣且易于理解。那么,让我们开始吧! ? ?什么是useEffect useEffect 是一个 React Hook,允许您在功能组件中执行副作用。副作用是在组件外部发生的操作...
    编程 发布于2024-11-06
  • 如何在 Google Cloud Platform 免费层上构建现代数据平台
    如何在 Google Cloud Platform 免费层上构建现代数据平台
    我在 Medium.com 上发布了一系列七篇免费公开文章“如何在 Google Cloud Platform 免费层上构建现代数据平台”。 主要文章位于:https://medium.com/@markwkiehl/building-a-data-platform-on-gcp-0427500f...
    编程 发布于2024-11-06
  • 帖子 #f 挣扎
    帖子 #f 挣扎
    这篇文章是关于我迄今为止在编码和学习方面的挣扎 一个。我只能保持专注一个小时,最多两个小时。 b.我很容易分心 c.我不能久坐,否则我会开始感到烦躁和休息腿部问题。 我想到的有助于解决问题的解决方案 一个。我需要开始更频繁地使用我的番茄工作法应用程序 B. 我开始将手机调成振动,如果我有另一个屏幕...
    编程 发布于2024-11-06
  • 面向 Web 开发人员的热门 Chrome 扩展 4
    面向 Web 开发人员的热门 Chrome 扩展 4
    2024 年最适合 Web 开发者的 10 款 Chrome 扩展 随着 2024 年的进展,Chrome 扩展程序已成为 Web 开发人员工具包中不可或缺的一部分,在浏览器中提供强大的功能。在这篇文章中,我们将探讨今年在 Web 开发社区掀起波澜的 10 大 Chrome 扩展程...
    编程 发布于2024-11-06
  • 如何使用 React Router v4/v5 嵌套路由:简化指南
    如何使用 React Router v4/v5 嵌套路由:简化指南
    React Router v4/v5 的嵌套路由:简化指南使用 React Router 时,嵌套路由是组织的关键技术您的应用程序的导航。然而,新手经常面临设置嵌套路由的挑战。本文旨在简化使用 React Router v4/v5 的过程。React Router v4 在路由嵌套方式上引入了重大转...
    编程 发布于2024-11-06
  • 如何使用 UTF8 字符编码保留 MySQL 中的表格式?
    如何使用 UTF8 字符编码保留 MySQL 中的表格式?
    使用 UTF8 字符编码增强 MySQL 命令行格式使用存储在数据库表中的瑞典语和挪威语字符串时,查询数据时可能会遇到表格式问题使用不同的字符集。问题陈述默认情况下,使用“set names latin1;”产生失真的输出: ----------------------------------- ...
    编程 发布于2024-11-06
  • CSS 盒子模型
    CSS 盒子模型
    CSS 盒子模型是 Web 开发中的一个基本概念,它构成了 Web 布局和设计的基础。它决定了元素的大小、内容的呈现方式以及它们在网页上如何相互交互。掌握盒模型对于任何使用 HTML 和 CSS 的开发人员来说都是至关重要的,因为它会影响元素的显示、间隔和对齐方式。 在本文中,我们将详细探讨 CSS...
    编程 发布于2024-11-06
  • 我如何编写 CSS 选择器
    我如何编写 CSS 选择器
    有很多 CSS 方法,但我讨厌它们。有些多(顺风等),有些少(BEM、OOCSS 等)。但归根结底,它们都有缺陷。 当然,人们使用这些方法有充分的理由,并且解决的许多问题我也遇到过。因此,在这篇文章中,我想写下我自己的关于如何保持 CSS 组织的指南。 这不是一个任何人都可以开始使用的完整描述的 C...
    编程 发布于2024-11-06
  • 为什么输入元素不支持 HTML5 中的 ::after 伪元素?
    为什么输入元素不支持 HTML5 中的 ::after 伪元素?
    ::before 和 ::after 的伪元素兼容性在 HTML5 中,::before 和 ::after 伪元素可以使用附加内容(例如图标或复选标记)增强元素。然而,并非所有元素都完全支持这些伪元素。输入元素和 ::after在提供的示例中,::after 伪元素不是显示在输入元素上。这是因为类...
    编程 发布于2024-11-06
  • 如何使用 PHP 确定特定时区的星期几?
    如何使用 PHP 确定特定时区的星期几?
    在 PHP 中确定指定时区的星期几在 PHP 中处理日期和时间时,处理起来可能具有挑战性时区并计算具体值,例如星期几。本文将指导您完成使用 PHP 查找特定时区中的星期几的过程。了解用户的时区要确定用户的时区,您需要使用 PHP 函数 date_default_timezone_get()。此函数返...
    编程 发布于2024-11-06
  • 如何在 Go 通道中有效地生成不同的值?
    如何在 Go 通道中有效地生成不同的值?
    在 Go Channel 中高效生成不同值在 Go 中,Channel 为并发通信提供了强大的机制。但是,在使用通道时,您可能会遇到需要过滤掉重复值或确保仅发出不同值的情况。本文探讨了创建仅输出唯一值的通道的有效方法。生成不同值的挑战考虑以下场景:您有一个通道接收多个值,并且您希望迭代它,同时仅打印...
    编程 发布于2024-11-06
  • 如何使用 Tailwind CSS 设置 os Next.js
    如何使用 Tailwind CSS 设置 os Next.js
    要使用 Tailwind CSS 设置 Next.js,请按照以下步骤操作: 第 1 步:创建一个新的 Next.js 项目 如果您尚未创建 Next.js 项目,您可以使用 create-next-app 创建一个项目。 npx create-next-app@latest my-...
    编程 发布于2024-11-06
  • 如何解决 PHPmailer HTML 内容渲染问题?
    如何解决 PHPmailer HTML 内容渲染问题?
    PHPmailer 无法渲染 HTML 内容使用 PHPmailer 发送电子邮件时,用户遇到 HTML 代码显示为原始文本的问题交货时。尽管使用了 IsHTML() 方法,所需的 HTML 内容仍然无法访问。潜在问题此行为背后的原因在于方法调用的顺序。与它的前身不同,PHPMailer 6 要求在...
    编程 发布于2024-11-06
  • 如何使用 Java 从 HTML 文档中提取数据?
    如何使用 Java 从 HTML 文档中提取数据?
    Java HTML解析要从网站获取数据,首先必须了解HTML文档的结构。 HTML 元素使用标签进行组织,标签指定每个元素的类型和内容。例如,以下 HTML 表示具有特定 CSS 类的 div 标签:<div class="classname"></div>...
    编程 发布于2024-11-06
  • 为什么 Java 异常处理代码会产生“132Exception in thread main MyExc1”而不是“13Exception in thread main MyExc2”?
    为什么 Java 异常处理代码会产生“132Exception in thread main MyExc1”而不是“13Exception in thread main MyExc2”?
    Java中的异常处理:解开歧义在一个令人费解的Java异常处理场景中,一个大学问题提出了以下代码片段: // Exception Heirarchy class MyExc1 extends Exception {} class MyExc2 extends Exception {} class M...
    编程 发布于2024-11-06

免责声明: 提供的所有资源部分来自互联网,如果有侵犯您的版权或其他权益,请说明详细缘由并提供版权或权益证明然后发到邮箱:[email protected] 我们会第一时间内为您处理。

Copyright© 2022 湘ICP备2022001581号-3