feat: enhance AbacatePay donation system with updated docs and form fields
Updated ABACATEPAY_SETUP.md with comprehensive setup guide, including environment variables, API key instructions, webhook configuration, donation flow, and field details for better integration clarity. Added required fields (phone, CPF/CNPJ) to the donation form in app/[locale]/apoiar/page.tsx to collect essential donor information and improve compliance. This ensures smoother setup and more robust donation processing.
This commit is contained in:
1 parent
00501e6699
commit
ef521876eb
9 files changed
+922
-168
No files matched your search
+87
-59
@@ -1,6 +1,5 @@
|
||||
interface AbacatePayConfig {
|
||||
apiKey: string;
|
||||
accountId: string;
|
||||
baseUrl?: string;
|
||||
}
|
||||
|
||||
@@ -19,13 +18,22 @@ interface BillingResponse {
|
||||
id: string;
|
||||
amount: number;
|
||||
status: "PENDING" | "PAID" | "EXPIRED" | "CANCELLED";
|
||||
pixCode?: string;
|
||||
pixQrCode?: string;
|
||||
qrCode?: string;
|
||||
brCode?: string; // Código PIX copia-e-cola
|
||||
brCodeBase64?: string; // QR Code em Base64
|
||||
expiresAt?: string;
|
||||
createdAt: string;
|
||||
url?: string; // URL para redirecionamento
|
||||
paymentUrl?: string; // URL alternativa de pagamento
|
||||
}
|
||||
|
||||
interface CreatePixParams {
|
||||
amount: number; // em centavos
|
||||
description: string;
|
||||
expiresIn?: number; // em segundos, padrão 600 (10 min)
|
||||
customer?: {
|
||||
name: string;
|
||||
email?: string;
|
||||
cellphone?: string;
|
||||
taxId?: string;
|
||||
};
|
||||
}
|
||||
|
||||
class AbacatePayClient {
|
||||
@@ -38,6 +46,46 @@ class AbacatePayClient {
|
||||
};
|
||||
}
|
||||
|
||||
async createPixQrCode(params: CreatePixParams): Promise<BillingResponse> {
|
||||
const requestBody: any = {
|
||||
amount: params.amount,
|
||||
expiresIn: params.expiresIn || 600, // 10 minutos por padrão
|
||||
description: params.description,
|
||||
};
|
||||
|
||||
if (params.customer) {
|
||||
requestBody.customer = params.customer;
|
||||
}
|
||||
|
||||
console.log("🔍 PIX Request:", JSON.stringify(requestBody, null, 2));
|
||||
|
||||
const response = await fetch(`${this.config.baseUrl}/pixQrCode/create`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
Authorization: `Bearer ${this.config.apiKey}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(requestBody),
|
||||
});
|
||||
|
||||
console.log("🔍 PIX Response Status:", response.status);
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}));
|
||||
console.error("🔍 PIX Error:", errorData);
|
||||
throw new Error(`Failed to create PIX: ${JSON.stringify(errorData)}`);
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
console.log("🔍 PIX Success:", result);
|
||||
|
||||
if (result.error) {
|
||||
throw new Error(result.error);
|
||||
}
|
||||
|
||||
return result.data;
|
||||
}
|
||||
|
||||
async createBilling(params: CreateBillingParams): Promise<BillingResponse> {
|
||||
const response = await fetch(`${this.config.baseUrl}/billing`, {
|
||||
method: "POST",
|
||||
@@ -46,7 +94,6 @@ class AbacatePayClient {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
account_id: this.config.accountId,
|
||||
amount: params.amount * 100, // centavos
|
||||
metadata: params.metadata,
|
||||
return_url: params.returnUrl,
|
||||
@@ -64,56 +111,6 @@ class AbacatePayClient {
|
||||
return response.json();
|
||||
}
|
||||
|
||||
async createDonationBilling(params: {
|
||||
amount: number;
|
||||
donorEmail?: string;
|
||||
donorName?: string;
|
||||
message?: string;
|
||||
}): Promise<BillingResponse> {
|
||||
const requestBody = {
|
||||
products: [
|
||||
{
|
||||
external_id: `donation-${Date.now()}`,
|
||||
name: "Doação - Tibia Trade Pagodera",
|
||||
quantity: 1,
|
||||
price: Math.round(params.amount * 100), // R$ para centavos
|
||||
},
|
||||
],
|
||||
return_url: `${process.env.NEXT_PUBLIC_BASE_URL}/apoiar/obrigado`,
|
||||
completion_url: `${process.env.NEXT_PUBLIC_BASE_URL}/api/donations/verify`,
|
||||
};
|
||||
|
||||
console.log("🔍 AbacatePay Request URL:", `${this.config.baseUrl}/billing`);
|
||||
console.log(
|
||||
"🔍 AbacatePay Request Body:",
|
||||
JSON.stringify(requestBody, null, 2)
|
||||
);
|
||||
|
||||
const response = await fetch(`${this.config.baseUrl}/billing`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
Authorization: `Bearer ${this.config.apiKey}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(requestBody),
|
||||
});
|
||||
|
||||
console.log("🔍 AbacatePay Response Status:", response.status);
|
||||
console.log("🔍 AbacatePay Response OK:", response.ok);
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}));
|
||||
console.error("🔍 AbacatePay Error Response:", errorData);
|
||||
throw new Error(
|
||||
`Failed to create billing: ${errorData.message || response.statusText}`
|
||||
);
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
console.log("🔍 AbacatePay Success Response:", result);
|
||||
return result;
|
||||
}
|
||||
|
||||
async getBillingStatus(billingId: string): Promise<BillingResponse> {
|
||||
const response = await fetch(
|
||||
`${this.config.baseUrl}/billing/${billingId}`,
|
||||
@@ -133,7 +130,39 @@ class AbacatePayClient {
|
||||
);
|
||||
}
|
||||
|
||||
return response.json();
|
||||
const result = await response.json();
|
||||
|
||||
// AbacatePay retorna { error: null, data: { ... } }
|
||||
if (result.error) {
|
||||
throw new Error(result.error);
|
||||
}
|
||||
|
||||
// Retornar apenas o data
|
||||
return result.data;
|
||||
}
|
||||
|
||||
async getPixStatus(pixId: string): Promise<BillingResponse> {
|
||||
// AbacatePay usa o endpoint /billing/{id} para todos os tipos de billing
|
||||
const response = await fetch(`${this.config.baseUrl}/billing/${pixId}`, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${this.config.apiKey}`,
|
||||
},
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}));
|
||||
throw new Error(
|
||||
`Failed to get PIX status: ${errorData.message || response.statusText}`
|
||||
);
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (result.error) {
|
||||
throw new Error(result.error);
|
||||
}
|
||||
|
||||
return result.data;
|
||||
}
|
||||
|
||||
verifyWebhookSignature(payload: string, signature: string): boolean {
|
||||
@@ -159,7 +188,6 @@ if (process.env.NODE_ENV !== "production") {
|
||||
|
||||
export const abacatePay = new AbacatePayClient({
|
||||
apiKey,
|
||||
accountId: process.env.ABACATEPAY_ACCOUNT_ID || "demo_account",
|
||||
});
|
||||
|
||||
export type { CreateBillingParams, BillingResponse };
|
||||
Reference in new issue
Block a user