File size: 2,557 Bytes
92bfa2a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
package controllers

import (
	"log"

	"abdanhafidz.com/go-boilerplate/models/dto"
	http_error "abdanhafidz.com/go-boilerplate/models/error"
	"abdanhafidz.com/go-boilerplate/services"
	"abdanhafidz.com/go-boilerplate/utils"
	"github.com/gin-gonic/gin"
)

type PaymentCallbackController interface {
	HandleCallback(ctx *gin.Context)
}

type paymentCallbackController struct {
	paymentService services.PaymentService
}

func NewPaymentCallbackController(

	paymentService services.PaymentService,

) PaymentCallbackController {
	return &paymentCallbackController{
		paymentService: paymentService,
	}
}

// Handle Payment Callback godoc
// @Summary      Handle Xendit Payment Callback
// @Description  Receive and process payment status updates from Xendit
// @Tags         Payment
// @Accept       json
// @Produce      json
// @Param        request  body      map[string]interface{}  true  "Xendit Callback Payload"
// @Success      200      {object}  dto.SuccessResponse[any]
// @Failure      400      {object}  dto.ErrorResponse
// @Router       /api/v1/payment/callback [post]
func (c *paymentCallbackController) HandleCallback(ctx *gin.Context) {
	// Xendit sends JSON payload
	// Basic structure for Invoice Callback:
	// { "id": "...", "external_id": "...", "status": "PAID", ... }
	var callbackData map[string]interface{}
	if err := ctx.ShouldBindJSON(&callbackData); err != nil {
		utils.ResponseFAILED(ctx, gin.H(nil), http_error.BAD_REQUEST_ERROR)
		return
	}

	log.Printf("Payment Callback Received: %+v", callbackData)

	status, ok := callbackData["status"].(string)
	if !ok {
		// Not a status update or unknown format
		var _ dto.SuccessResponse[any]

		ResponseJSON(ctx, gin.H(nil), "Ignored: No status", nil)
		return
	}

	invoiceId, _ := callbackData["id"].(string)

	if status == "PAID" || status == "SETTLED" {
		// Handle Event Payment
		// We need a method in Service to handle "ConfirmPayment" by InvoiceID
		// But existing services don't have it.
		// Let's add it to PaymentService? Yes.
		err := c.paymentService.ConfirmPayment(ctx.Request.Context(), invoiceId)
		if err != nil {
			log.Printf("Payment Confirmation Failed: %v", err)
			// Don't return error to Xendit if logic failed, but maybe we should?
			// Xendit expects 200 OK.
		}
	} else if status == "EXPIRED" {
		c.paymentService.ExpirePayment(ctx.Request.Context(), invoiceId)
	}

	// Always return 200 to Xendit
	ResponseJSON(ctx, gin.H(nil), gin.H{"callback": "Callback Received"}, nil)
}