diff --git a/internal/domain/chapa.go b/internal/domain/chapa.go index 758279e..d74a687 100644 --- a/internal/domain/chapa.go +++ b/internal/domain/chapa.go @@ -1,5 +1,41 @@ package domain +import ( + "encoding/json" + "fmt" +) + +// ChapaFlexibleString unmarshals JSON string or number (Chapa verify/webhook payloads vary). +type ChapaFlexibleString string + +func (s *ChapaFlexibleString) UnmarshalJSON(data []byte) error { + if len(data) == 0 || string(data) == "null" { + *s = "" + return nil + } + switch data[0] { + case '"': + var v string + if err := json.Unmarshal(data, &v); err != nil { + return err + } + *s = ChapaFlexibleString(v) + case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-': + var n json.Number + if err := json.Unmarshal(data, &n); err != nil { + return err + } + *s = ChapaFlexibleString(n.String()) + default: + return fmt.Errorf("chapa flexible string: unsupported json type %q", data[0]) + } + return nil +} + +func (s ChapaFlexibleString) String() string { + return string(s) +} + // ChapaInitializeRequest is sent to POST /transaction/initialize. type ChapaInitializeRequest struct { Amount string `json:"amount"` @@ -32,13 +68,13 @@ type ChapaVerifyResponse struct { } type ChapaTransactionData struct { - TxRef string `json:"tx_ref"` - Reference string `json:"reference"` - Amount string `json:"amount"` - Currency string `json:"currency"` - Status string `json:"status"` - PaymentMethod string `json:"payment_method"` - Mode string `json:"mode"` + TxRef string `json:"tx_ref"` + Reference string `json:"reference"` + Amount ChapaFlexibleString `json:"amount"` + Currency string `json:"currency"` + Status string `json:"status"` + PaymentMethod string `json:"payment_method"` + Mode string `json:"mode"` } // ChapaWebhookPayload is the body POSTed to the webhook URL. @@ -48,7 +84,7 @@ type ChapaWebhookPayload struct { TxRef string `json:"tx_ref"` Reference string `json:"reference"` Status string `json:"status"` - Amount string `json:"amount"` + Amount ChapaFlexibleString `json:"amount"` Currency string `json:"currency"` PaymentMethod string `json:"payment_method"` Mode string `json:"mode"` diff --git a/internal/domain/chapa_test.go b/internal/domain/chapa_test.go new file mode 100644 index 0000000..7a1b3f1 --- /dev/null +++ b/internal/domain/chapa_test.go @@ -0,0 +1,44 @@ +package domain + +import ( + "encoding/json" + "testing" +) + +func TestChapaFlexibleString_UnmarshalJSON(t *testing.T) { + tests := []struct { + name string + raw string + want string + wantErr bool + }{ + {name: "string amount", raw: `"500.00"`, want: "500.00"}, + {name: "number amount", raw: `500`, want: "500"}, + {name: "float amount", raw: `499.99`, want: "499.99"}, + {name: "null", raw: `null`, want: ""}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + var s ChapaFlexibleString + err := json.Unmarshal([]byte(tt.raw), &s) + if (err != nil) != tt.wantErr { + t.Fatalf("err=%v wantErr=%v", err, tt.wantErr) + } + if s.String() != tt.want { + t.Fatalf("got %q want %q", s.String(), tt.want) + } + }) + } +} + +func TestChapaVerifyResponse_UnmarshalNumberAmount(t *testing.T) { + raw := `{"status":"success","message":"ok","data":{"tx_ref":"tx-1","reference":"ref-1","amount":500,"currency":"ETB","status":"success","payment_method":"telebirr"}}` + var resp ChapaVerifyResponse + if err := json.Unmarshal([]byte(raw), &resp); err != nil { + t.Fatalf("unmarshal: %v", err) + } + if resp.Data.Amount.String() != "500" { + t.Fatalf("amount=%q want 500", resp.Data.Amount.String()) + } +}