Backend: - Renomeado BACKEND_URL para BACKEND_HOST no .env e nas configs para consistência. - Atualizado MercadoPagoGateway para usar o BACKEND_HOST correto na notification_url. - Atualizado payment_handler para receber e processar informações do Pagador (email/doc). - Corrigido erro 500 ao buscar dados de compradores B2B. Frontend: - Criado componente Header reutilizável e aplicado nas páginas internas. - Implementada nova página "Meus Pedidos" com lógica de listagem correta. - Implementada página de "Detalhes do Pedido" (/pedidos/[id]) com alto contraste visual. - Melhorada a legibilidade da página de detalhes (textos pretos/escuros). - Corrigido bug onde pagamentos rejeitados eram tratados como sucesso (agora verifica status 'rejected' no serviço). - Adicionado componente <Toaster /> ao layout principal para corrigir notificações invisíveis. - Adicionado feedback visual persistente de erro na tela de checkout para falhas de pagamento.
330 lines
9.7 KiB
Go
330 lines
9.7 KiB
Go
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"database/sql"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"log"
|
|
"net/http"
|
|
"time"
|
|
|
|
"github.com/gofrs/uuid/v5"
|
|
_ "github.com/jackc/pgx/v5/stdlib"
|
|
)
|
|
|
|
const (
|
|
baseURL = "http://localhost:8214/api/v1"
|
|
dbURL = "postgres://postgres:123@localhost:55432/saveinmed?sslmode=disable"
|
|
)
|
|
|
|
type LoginResponse struct {
|
|
Token string `json:"access_token"`
|
|
}
|
|
|
|
type PaymentPreferenceResponse struct {
|
|
PaymentURL string `json:"payment_url"`
|
|
MarketplaceFee int64 `json:"marketplace_fee"`
|
|
SellerReceivable int64 `json:"seller_receivable"`
|
|
}
|
|
|
|
func main() {
|
|
log.Println("Starting Split Payment Verification...")
|
|
|
|
db, err := sql.Open("pgx", dbURL)
|
|
if err != nil {
|
|
log.Fatalf("Failed to connect to DB: %v", err)
|
|
}
|
|
defer db.Close()
|
|
|
|
// 1. Login as Admin
|
|
adminToken := login("andre.fr93@gmail.com", "teste1234")
|
|
log.Println("Logged in as Admin.")
|
|
|
|
// 2. Create Seller Company & Account
|
|
sellerID := createCompany(adminToken, "Farmácia Vendedora")
|
|
log.Printf("Created Seller Company: %s", sellerID)
|
|
|
|
dummyAccountID := "1942948243"
|
|
_, err = db.Exec(`
|
|
INSERT INTO seller_payment_accounts (seller_id, gateway, account_id, account_type, status, created_at)
|
|
VALUES ($1, 'mercadopago', $2, 'standard', 'active', now())
|
|
ON CONFLICT (seller_id, gateway) DO UPDATE
|
|
SET account_id = $2, status = 'active'
|
|
`, sellerID, dummyAccountID)
|
|
if err != nil {
|
|
log.Fatalf("Failed to inject SellerPaymentAccount: %v", err)
|
|
}
|
|
|
|
// 3. Create Product & Inventory (As Admin acting for Seller)
|
|
productID := createProduct(adminToken, sellerID)
|
|
log.Printf("Created Product: %s", productID)
|
|
createInventory(adminToken, sellerID, productID)
|
|
|
|
// 4. Create Buyer Company & User
|
|
buyerCompanyID := createCompany(adminToken, "Farmácia Compradora")
|
|
buyerEmail := fmt.Sprintf("buyer_%d@test.com", time.Now().Unix())
|
|
createUser(adminToken, buyerCompanyID, buyerEmail, "Dono")
|
|
log.Printf("Created Buyer User: %s", buyerEmail)
|
|
|
|
// 5. Login as Buyer
|
|
buyerToken := login(buyerEmail, "123456")
|
|
log.Println("Logged in as Buyer.")
|
|
|
|
// 6. Buyer adds to Cart
|
|
addToCart(buyerToken, productID)
|
|
log.Println("Added to Cart.")
|
|
|
|
// 7. Buyer creates Address (Shipping)
|
|
// Note: CreateOrder requires Shipping Object. We can construct it.
|
|
// But usually we pick from existing addresses.
|
|
// Let's passed mocked shipping data in CreateOrder directly.
|
|
|
|
// 8. Create Order
|
|
orderID := createOrder(buyerToken, sellerID, productID)
|
|
log.Printf("Created Order: %s", orderID)
|
|
|
|
// 9. Payment Preference
|
|
pref := createPaymentPreference(buyerToken, orderID)
|
|
log.Printf("✅ SUCCESS! Payment URL generated: %s", pref.PaymentURL)
|
|
log.Printf(" Marketplace Fee: %d cents", pref.MarketplaceFee)
|
|
log.Printf(" Seller Receivable: %d cents", pref.SellerReceivable)
|
|
}
|
|
|
|
func login(email, password string) string {
|
|
payload := map[string]string{
|
|
"email": email,
|
|
"password": password,
|
|
}
|
|
body, _ := json.Marshal(payload)
|
|
resp, err := http.Post(baseURL+"/auth/login", "application/json", bytes.NewBuffer(body))
|
|
if err != nil {
|
|
log.Fatalf("Login failed: %v", err)
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode != 200 {
|
|
b, _ := io.ReadAll(resp.Body)
|
|
log.Fatalf("Login failed status %d: %s", resp.StatusCode, string(b))
|
|
}
|
|
|
|
var res LoginResponse
|
|
json.NewDecoder(resp.Body).Decode(&res)
|
|
return res.Token
|
|
}
|
|
|
|
func createCompany(token, name string) uuid.UUID {
|
|
cnpj := fmt.Sprintf("%d", time.Now().UnixNano())[:14]
|
|
payload := map[string]interface{}{
|
|
"corporate_name": name,
|
|
"cnpj": cnpj,
|
|
"category": "farmacia",
|
|
"email": "comp" + cnpj + "@test.com",
|
|
}
|
|
body, _ := json.Marshal(payload)
|
|
req, _ := http.NewRequest("POST", baseURL+"/companies", bytes.NewBuffer(body))
|
|
req.Header.Set("Authorization", "Bearer "+token)
|
|
req.Header.Set("Content-Type", "application/json")
|
|
|
|
client := &http.Client{}
|
|
resp, err := client.Do(req)
|
|
if err != nil {
|
|
log.Fatalf("CreateCompany failed: %v", err)
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode != 200 && resp.StatusCode != 201 {
|
|
b, _ := io.ReadAll(resp.Body)
|
|
log.Fatalf("CreateCompany failed status %d: %s", resp.StatusCode, string(b))
|
|
}
|
|
|
|
var m map[string]interface{}
|
|
json.NewDecoder(resp.Body).Decode(&m)
|
|
idStr, _ := m["id"].(string)
|
|
return uuid.FromStringOrNil(idStr)
|
|
}
|
|
|
|
func createUser(token string, companyID uuid.UUID, email, role string) {
|
|
payload := map[string]interface{}{
|
|
"company_id": companyID.String(),
|
|
"role": role,
|
|
"name": "User Test",
|
|
"username": email, // simple username
|
|
"email": email,
|
|
"password": "123456",
|
|
"cpf": fmt.Sprintf("%d", time.Now().UnixNano())[:11],
|
|
}
|
|
body, _ := json.Marshal(payload)
|
|
req, _ := http.NewRequest("POST", baseURL+"/users", bytes.NewBuffer(body))
|
|
req.Header.Set("Authorization", "Bearer "+token)
|
|
req.Header.Set("Content-Type", "application/json")
|
|
|
|
client := &http.Client{}
|
|
resp, err := client.Do(req)
|
|
if err != nil {
|
|
log.Fatalf("CreateUser failed: %v", err)
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode != 201 {
|
|
b, _ := io.ReadAll(resp.Body)
|
|
log.Fatalf("CreateUser failed status %d: %s", resp.StatusCode, string(b))
|
|
}
|
|
}
|
|
|
|
func createProduct(token string, sellerID uuid.UUID) uuid.UUID {
|
|
payload := map[string]interface{}{
|
|
"name": "Produto Teste Split",
|
|
"price_cents": 10000,
|
|
"seller_id": sellerID.String(),
|
|
"manufacturer": "Lab Test",
|
|
"ean_code": fmt.Sprintf("%d", time.Now().Unix()),
|
|
"category": "Medicamentos",
|
|
"subcategory": "Analgesicos",
|
|
"description": "Produto teste",
|
|
"internal_code": "TEST-" + fmt.Sprintf("%d", time.Now().Unix()),
|
|
}
|
|
body, _ := json.Marshal(payload)
|
|
req, _ := http.NewRequest("POST", baseURL+"/products", bytes.NewBuffer(body))
|
|
req.Header.Set("Authorization", "Bearer "+token)
|
|
req.Header.Set("Content-Type", "application/json")
|
|
|
|
client := &http.Client{}
|
|
resp, err := client.Do(req)
|
|
if err != nil {
|
|
log.Fatalf("CreateProduct failed: %v", err)
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode != 200 && resp.StatusCode != 201 {
|
|
b, _ := io.ReadAll(resp.Body)
|
|
log.Fatalf("CreateProduct failed status %d: %s", resp.StatusCode, string(b))
|
|
}
|
|
|
|
var m map[string]interface{}
|
|
json.NewDecoder(resp.Body).Decode(&m)
|
|
idStr, _ := m["id"].(string)
|
|
return uuid.FromStringOrNil(idStr)
|
|
}
|
|
|
|
func createInventory(token string, sellerID, productID uuid.UUID) {
|
|
payload := map[string]interface{}{
|
|
"product_id": productID.String(),
|
|
"seller_id": sellerID.String(),
|
|
"sale_price_cents": 10000,
|
|
"stock_quantity": 100,
|
|
"expires_at": time.Now().Add(24 * time.Hour).Format(time.RFC3339),
|
|
"observations": "Stock test",
|
|
}
|
|
body, _ := json.Marshal(payload)
|
|
req, _ := http.NewRequest("POST", baseURL+"/inventory", bytes.NewBuffer(body))
|
|
req.Header.Set("Authorization", "Bearer "+token)
|
|
req.Header.Set("Content-Type", "application/json")
|
|
|
|
client := &http.Client{}
|
|
resp, err := client.Do(req)
|
|
if err != nil {
|
|
log.Fatalf("CreateInventory failed: %v", err)
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode != 200 && resp.StatusCode != 201 {
|
|
b, _ := io.ReadAll(resp.Body)
|
|
log.Fatalf("CreateInventory failed status %d: %s", resp.StatusCode, string(b))
|
|
}
|
|
}
|
|
|
|
func addToCart(token string, productID uuid.UUID) {
|
|
payload := map[string]interface{}{
|
|
"product_id": productID.String(),
|
|
"quantity": 1,
|
|
}
|
|
body, _ := json.Marshal(payload)
|
|
req, _ := http.NewRequest("POST", baseURL+"/cart", bytes.NewBuffer(body))
|
|
req.Header.Set("Authorization", "Bearer "+token)
|
|
req.Header.Set("Content-Type", "application/json")
|
|
|
|
client := &http.Client{}
|
|
resp, err := client.Do(req)
|
|
if err != nil {
|
|
log.Fatalf("AddToCart failed: %v", err)
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode != 200 && resp.StatusCode != 201 {
|
|
b, _ := io.ReadAll(resp.Body)
|
|
log.Fatalf("AddToCart failed status %d: %s", resp.StatusCode, string(b))
|
|
}
|
|
}
|
|
|
|
func createOrder(token string, sellerID, productID uuid.UUID) uuid.UUID {
|
|
payload := map[string]interface{}{
|
|
"seller_id": sellerID.String(),
|
|
"items": []map[string]interface{}{
|
|
{
|
|
"product_id": productID.String(),
|
|
"quantity": 1,
|
|
"unit_cents": 10000,
|
|
},
|
|
},
|
|
"shipping": map[string]interface{}{
|
|
"titulo": "Casa",
|
|
"zip_code": "01001000",
|
|
"street": "Praça da Sé",
|
|
"number": "1",
|
|
"district": "Sé",
|
|
"city": "São Paulo",
|
|
"state": "SP",
|
|
"country": "BR",
|
|
},
|
|
"payment_method": map[string]interface{}{
|
|
"type": "credit_card",
|
|
"installments": 1,
|
|
},
|
|
}
|
|
body, _ := json.Marshal(payload)
|
|
req, _ := http.NewRequest("POST", baseURL+"/orders", bytes.NewBuffer(body))
|
|
req.Header.Set("Authorization", "Bearer "+token)
|
|
req.Header.Set("Content-Type", "application/json")
|
|
|
|
client := &http.Client{}
|
|
resp, err := client.Do(req)
|
|
if err != nil {
|
|
log.Fatalf("CreateOrder failed: %v", err)
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode != 200 && resp.StatusCode != 201 {
|
|
b, _ := io.ReadAll(resp.Body)
|
|
log.Fatalf("CreateOrder failed status %d: %s", resp.StatusCode, string(b))
|
|
}
|
|
|
|
var m map[string]interface{}
|
|
json.NewDecoder(resp.Body).Decode(&m)
|
|
idStr, _ := m["id"].(string)
|
|
return uuid.FromStringOrNil(idStr)
|
|
}
|
|
|
|
func createPaymentPreference(token string, orderID uuid.UUID) PaymentPreferenceResponse {
|
|
req, _ := http.NewRequest("POST", fmt.Sprintf("%s/orders/%s/payment", baseURL, orderID.String()), nil)
|
|
req.Header.Set("Authorization", "Bearer "+token)
|
|
|
|
client := &http.Client{}
|
|
resp, err := client.Do(req)
|
|
if err != nil {
|
|
log.Fatalf("CreatePaymentPreference failed: %v", err)
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
bodyBytes, _ := io.ReadAll(resp.Body)
|
|
if resp.StatusCode != 200 && resp.StatusCode != 201 {
|
|
log.Fatalf("CreatePaymentPreference failed status %d: %s", resp.StatusCode, string(bodyBytes))
|
|
}
|
|
|
|
var res PaymentPreferenceResponse
|
|
if err := json.Unmarshal(bodyBytes, &res); err != nil {
|
|
log.Fatalf("Failed to decode response: %v, body: %s", err, string(bodyBytes))
|
|
}
|
|
return res
|
|
}
|