santimpay direct payment

This commit is contained in:
Yared Yemane 2025-08-18 16:35:33 +03:00
parent f32331bcdb
commit d40bdcf33c
11 changed files with 1841 additions and 76 deletions

View File

@ -153,7 +153,7 @@ func main() {
virtualGameSvc := virtualgameservice.New(vitualGameRepo, *walletSvc, store, cfg, logger)
aleaService := alea.NewAleaPlayService(vitualGameRepo, *walletSvc, cfg, logger)
veliCLient := veli.NewClient(cfg, walletSvc)
veliVirtualGameService := veli.New(veliCLient, walletSvc,cfg)
veliVirtualGameService := veli.New(veliCLient, walletSvc, cfg)
recommendationSvc := recommendation.NewService(recommendationRepo)
chapaClient := chapa.NewClient(cfg.CHAPA_BASE_URL, cfg.CHAPA_SECRET_KEY)
@ -239,7 +239,7 @@ func main() {
santimpayClient := santimpay.NewSantimPayClient(cfg)
santimpaySvc := santimpay.NewSantimPayService(santimpayClient, cfg, transferStore)
santimpaySvc := santimpay.NewSantimPayService(santimpayClient, cfg, transferStore, walletSvc)
telebirrSvc := telebirr.NewTelebirrService(cfg, transferStore, walletSvc)
// Initialize and start HTTP server

View File

@ -4311,6 +4311,173 @@ const docTemplate = `{
}
}
},
"/api/v1/santimpay/b2c-withdrawal": {
"post": {
"description": "Initiates a B2C withdrawal request with SantimPay and returns the response.",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"SantimPay"
],
"summary": "Process SantimPay B2C Withdrawal",
"parameters": [
{
"description": "SantimPay B2C withdrawal request payload",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/domain.GeneratePaymentURLRequest"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/domain.Response"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/domain.ErrorResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/domain.ErrorResponse"
}
}
}
}
},
"/api/v1/santimpay/b2c/partners": {
"get": {
"description": "Fetches a list of available B2C payout partners (e.g., Telebirr, Mpesa, Banks) from SantimPay.",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"SantimPay"
],
"summary": "Get SantimPay B2C Partners",
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/domain.Response"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/domain.ErrorResponse"
}
}
}
}
},
"/api/v1/santimpay/callback": {
"post": {
"description": "Processes a callback from SantimPay, updates transfer status, and credits user wallet if payment was successful.",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"SantimPay"
],
"summary": "Process SantimPay Payment Callback",
"parameters": [
{
"description": "SantimPay callback payload",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/domain.SantimPayCallbackPayload"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/domain.Response"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/domain.ErrorResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/domain.ErrorResponse"
}
}
}
}
},
"/api/v1/santimpay/direct-payment": {
"post": {
"description": "Initiates a direct payment request with SantimPay and returns the response.",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"SantimPay"
],
"summary": "Process SantimPay Direct Payment",
"parameters": [
{
"description": "SantimPay direct payment request payload",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/domain.GeneratePaymentURLRequest"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/domain.Response"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/domain.ErrorResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/domain.ErrorResponse"
}
}
}
}
},
"/api/v1/santimpay/payment": {
"post": {
"description": "Generates a payment URL using SantimPay and returns it to the client.",
@ -4331,7 +4498,53 @@ const docTemplate = `{
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/domain.GeneratePaymentURLInput"
"$ref": "#/definitions/domain.GeneratePaymentURLRequest"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/domain.Response"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/domain.ErrorResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/domain.ErrorResponse"
}
}
}
}
},
"/api/v1/santimpay/transaction-status": {
"post": {
"description": "Retrieves the real-time status of a transaction from SantimPay.",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"SantimPay"
],
"summary": "Check SantimPay Transaction Status",
"parameters": [
{
"description": "Transaction status request payload",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/domain.TransactionStatusRequest"
}
}
],
@ -5434,7 +5647,7 @@ const docTemplate = `{
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/domain.GeneratePaymentURLInput"
"$ref": "#/definitions/domain.GeneratePaymentURLRequest"
}
}
],
@ -6443,6 +6656,64 @@ const docTemplate = `{
}
}
},
"/api/v1/veli/huge-wins": {
"post": {
"description": "Retrieves huge win transactions based on brand configuration (e.g. win \u003e 10000 USD or 100x bet)",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"Virtual Games - VeliGames"
],
"summary": "Get Veli Huge Wins",
"parameters": [
{
"description": "Huge Wins Request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/domain.HugeWinsRequest"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"allOf": [
{
"$ref": "#/definitions/domain.Response"
},
{
"type": "object",
"properties": {
"data": {
"$ref": "#/definitions/domain.HugeWinsResponse"
}
}
}
]
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/domain.ErrorResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/domain.ErrorResponse"
}
}
}
}
},
"/api/v1/veli/providers": {
"post": {
"description": "Retrieves the list of VeliGames providers",
@ -8270,10 +8541,6 @@ const docTemplate = `{
"domain.GamingActivityRequest": {
"type": "object",
"properties": {
"brandId": {
"description": "Required",
"type": "string"
},
"currencies": {
"description": "Optional",
"type": "array",
@ -8335,20 +8602,20 @@ const docTemplate = `{
}
}
},
"domain.GeneratePaymentURLInput": {
"domain.GeneratePaymentURLRequest": {
"type": "object",
"properties": {
"amount": {
"type": "integer"
},
"id": {
"paymentMethod": {
"type": "string"
},
"paymentReason": {
"type": "string"
},
"phoneNumber": {
"type": "string"
},
"reason": {
"type": "string"
}
}
},
@ -8401,6 +8668,108 @@ const docTemplate = `{
}
}
},
"domain.HugeWinItem": {
"type": "object",
"properties": {
"betAmount": {
"type": "number"
},
"betAmountUsd": {
"type": "number"
},
"betTransactionId": {
"type": "string"
},
"brandId": {
"type": "string"
},
"correlationId": {
"type": "string"
},
"createdAt": {
"type": "string"
},
"currency": {
"type": "string"
},
"gameId": {
"type": "string"
},
"operatorId": {
"type": "string"
},
"playerId": {
"type": "string"
},
"providerId": {
"type": "string"
},
"reason": {
"type": "string"
},
"roundId": {
"type": "string"
},
"winAmount": {
"type": "number"
},
"winAmountUsd": {
"type": "number"
},
"winTransactionId": {
"type": "string"
}
}
},
"domain.HugeWinsRequest": {
"type": "object",
"properties": {
"brandId": {
"type": "string"
},
"currencies": {
"type": "array",
"items": {
"type": "string"
}
},
"fromDate": {
"type": "string"
},
"gameIds": {
"type": "array",
"items": {
"type": "string"
}
},
"page": {
"type": "integer"
},
"providerId": {
"type": "string"
},
"size": {
"type": "integer"
},
"toDate": {
"type": "string"
}
}
},
"domain.HugeWinsResponse": {
"type": "object",
"properties": {
"items": {
"type": "array",
"items": {
"$ref": "#/definitions/domain.HugeWinItem"
}
},
"meta": {
"$ref": "#/definitions/domain.PaginationMeta"
}
}
},
"domain.InstResponse": {
"type": "object",
"properties": {
@ -8919,6 +9288,68 @@ const docTemplate = `{
"RoleCashier"
]
},
"domain.SantimPayCallbackPayload": {
"type": "object",
"properties": {
"accountNumber": {
"type": "string"
},
"address": {
"type": "string"
},
"amount": {
"type": "string"
},
"created_at": {
"type": "string"
},
"currency": {
"type": "string"
},
"failureRedirectUrl": {
"type": "string"
},
"merId": {
"type": "string"
},
"merName": {
"type": "string"
},
"message": {
"type": "string"
},
"msisdn": {
"type": "string"
},
"paymentVia": {
"type": "string"
},
"reason": {
"type": "string"
},
"receiverWalletID": {
"type": "string"
},
"refId": {
"type": "string"
},
"status": {
"type": "string"
},
"successRedirectUrl": {
"type": "string"
},
"thirdPartyId": {
"type": "string"
},
"txnId": {
"type": "string"
},
"updated_at": {
"type": "string"
}
}
},
"domain.ShopBetReq": {
"type": "object",
"properties": {
@ -9383,6 +9814,17 @@ const docTemplate = `{
}
}
},
"domain.TransactionStatusRequest": {
"type": "object",
"properties": {
"fullParams": {
"type": "boolean"
},
"id": {
"type": "string"
}
}
},
"domain.UpcomingEvent": {
"type": "object",
"properties": {

View File

@ -4303,6 +4303,173 @@
}
}
},
"/api/v1/santimpay/b2c-withdrawal": {
"post": {
"description": "Initiates a B2C withdrawal request with SantimPay and returns the response.",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"SantimPay"
],
"summary": "Process SantimPay B2C Withdrawal",
"parameters": [
{
"description": "SantimPay B2C withdrawal request payload",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/domain.GeneratePaymentURLRequest"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/domain.Response"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/domain.ErrorResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/domain.ErrorResponse"
}
}
}
}
},
"/api/v1/santimpay/b2c/partners": {
"get": {
"description": "Fetches a list of available B2C payout partners (e.g., Telebirr, Mpesa, Banks) from SantimPay.",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"SantimPay"
],
"summary": "Get SantimPay B2C Partners",
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/domain.Response"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/domain.ErrorResponse"
}
}
}
}
},
"/api/v1/santimpay/callback": {
"post": {
"description": "Processes a callback from SantimPay, updates transfer status, and credits user wallet if payment was successful.",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"SantimPay"
],
"summary": "Process SantimPay Payment Callback",
"parameters": [
{
"description": "SantimPay callback payload",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/domain.SantimPayCallbackPayload"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/domain.Response"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/domain.ErrorResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/domain.ErrorResponse"
}
}
}
}
},
"/api/v1/santimpay/direct-payment": {
"post": {
"description": "Initiates a direct payment request with SantimPay and returns the response.",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"SantimPay"
],
"summary": "Process SantimPay Direct Payment",
"parameters": [
{
"description": "SantimPay direct payment request payload",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/domain.GeneratePaymentURLRequest"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/domain.Response"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/domain.ErrorResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/domain.ErrorResponse"
}
}
}
}
},
"/api/v1/santimpay/payment": {
"post": {
"description": "Generates a payment URL using SantimPay and returns it to the client.",
@ -4323,7 +4490,53 @@
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/domain.GeneratePaymentURLInput"
"$ref": "#/definitions/domain.GeneratePaymentURLRequest"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/domain.Response"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/domain.ErrorResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/domain.ErrorResponse"
}
}
}
}
},
"/api/v1/santimpay/transaction-status": {
"post": {
"description": "Retrieves the real-time status of a transaction from SantimPay.",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"SantimPay"
],
"summary": "Check SantimPay Transaction Status",
"parameters": [
{
"description": "Transaction status request payload",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/domain.TransactionStatusRequest"
}
}
],
@ -5426,7 +5639,7 @@
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/domain.GeneratePaymentURLInput"
"$ref": "#/definitions/domain.GeneratePaymentURLRequest"
}
}
],
@ -6435,6 +6648,64 @@
}
}
},
"/api/v1/veli/huge-wins": {
"post": {
"description": "Retrieves huge win transactions based on brand configuration (e.g. win \u003e 10000 USD or 100x bet)",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"Virtual Games - VeliGames"
],
"summary": "Get Veli Huge Wins",
"parameters": [
{
"description": "Huge Wins Request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/domain.HugeWinsRequest"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"allOf": [
{
"$ref": "#/definitions/domain.Response"
},
{
"type": "object",
"properties": {
"data": {
"$ref": "#/definitions/domain.HugeWinsResponse"
}
}
}
]
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/domain.ErrorResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/domain.ErrorResponse"
}
}
}
}
},
"/api/v1/veli/providers": {
"post": {
"description": "Retrieves the list of VeliGames providers",
@ -8262,10 +8533,6 @@
"domain.GamingActivityRequest": {
"type": "object",
"properties": {
"brandId": {
"description": "Required",
"type": "string"
},
"currencies": {
"description": "Optional",
"type": "array",
@ -8327,20 +8594,20 @@
}
}
},
"domain.GeneratePaymentURLInput": {
"domain.GeneratePaymentURLRequest": {
"type": "object",
"properties": {
"amount": {
"type": "integer"
},
"id": {
"paymentMethod": {
"type": "string"
},
"paymentReason": {
"type": "string"
},
"phoneNumber": {
"type": "string"
},
"reason": {
"type": "string"
}
}
},
@ -8393,6 +8660,108 @@
}
}
},
"domain.HugeWinItem": {
"type": "object",
"properties": {
"betAmount": {
"type": "number"
},
"betAmountUsd": {
"type": "number"
},
"betTransactionId": {
"type": "string"
},
"brandId": {
"type": "string"
},
"correlationId": {
"type": "string"
},
"createdAt": {
"type": "string"
},
"currency": {
"type": "string"
},
"gameId": {
"type": "string"
},
"operatorId": {
"type": "string"
},
"playerId": {
"type": "string"
},
"providerId": {
"type": "string"
},
"reason": {
"type": "string"
},
"roundId": {
"type": "string"
},
"winAmount": {
"type": "number"
},
"winAmountUsd": {
"type": "number"
},
"winTransactionId": {
"type": "string"
}
}
},
"domain.HugeWinsRequest": {
"type": "object",
"properties": {
"brandId": {
"type": "string"
},
"currencies": {
"type": "array",
"items": {
"type": "string"
}
},
"fromDate": {
"type": "string"
},
"gameIds": {
"type": "array",
"items": {
"type": "string"
}
},
"page": {
"type": "integer"
},
"providerId": {
"type": "string"
},
"size": {
"type": "integer"
},
"toDate": {
"type": "string"
}
}
},
"domain.HugeWinsResponse": {
"type": "object",
"properties": {
"items": {
"type": "array",
"items": {
"$ref": "#/definitions/domain.HugeWinItem"
}
},
"meta": {
"$ref": "#/definitions/domain.PaginationMeta"
}
}
},
"domain.InstResponse": {
"type": "object",
"properties": {
@ -8911,6 +9280,68 @@
"RoleCashier"
]
},
"domain.SantimPayCallbackPayload": {
"type": "object",
"properties": {
"accountNumber": {
"type": "string"
},
"address": {
"type": "string"
},
"amount": {
"type": "string"
},
"created_at": {
"type": "string"
},
"currency": {
"type": "string"
},
"failureRedirectUrl": {
"type": "string"
},
"merId": {
"type": "string"
},
"merName": {
"type": "string"
},
"message": {
"type": "string"
},
"msisdn": {
"type": "string"
},
"paymentVia": {
"type": "string"
},
"reason": {
"type": "string"
},
"receiverWalletID": {
"type": "string"
},
"refId": {
"type": "string"
},
"status": {
"type": "string"
},
"successRedirectUrl": {
"type": "string"
},
"thirdPartyId": {
"type": "string"
},
"txnId": {
"type": "string"
},
"updated_at": {
"type": "string"
}
}
},
"domain.ShopBetReq": {
"type": "object",
"properties": {
@ -9375,6 +9806,17 @@
}
}
},
"domain.TransactionStatusRequest": {
"type": "object",
"properties": {
"fullParams": {
"type": "boolean"
},
"id": {
"type": "string"
}
}
},
"domain.UpcomingEvent": {
"type": "object",
"properties": {

View File

@ -770,9 +770,6 @@ definitions:
type: object
domain.GamingActivityRequest:
properties:
brandId:
description: Required
type: string
currencies:
description: Optional
items:
@ -816,16 +813,16 @@ definitions:
meta:
$ref: '#/definitions/domain.PaginationMeta'
type: object
domain.GeneratePaymentURLInput:
domain.GeneratePaymentURLRequest:
properties:
amount:
type: integer
id:
paymentMethod:
type: string
paymentReason:
type: string
phoneNumber:
type: string
reason:
type: string
type: object
domain.GetCompanyRes:
properties:
@ -863,6 +860,73 @@ definitions:
example: 1
type: integer
type: object
domain.HugeWinItem:
properties:
betAmount:
type: number
betAmountUsd:
type: number
betTransactionId:
type: string
brandId:
type: string
correlationId:
type: string
createdAt:
type: string
currency:
type: string
gameId:
type: string
operatorId:
type: string
playerId:
type: string
providerId:
type: string
reason:
type: string
roundId:
type: string
winAmount:
type: number
winAmountUsd:
type: number
winTransactionId:
type: string
type: object
domain.HugeWinsRequest:
properties:
brandId:
type: string
currencies:
items:
type: string
type: array
fromDate:
type: string
gameIds:
items:
type: string
type: array
page:
type: integer
providerId:
type: string
size:
type: integer
toDate:
type: string
type: object
domain.HugeWinsResponse:
properties:
items:
items:
$ref: '#/definitions/domain.HugeWinItem'
type: array
meta:
$ref: '#/definitions/domain.PaginationMeta'
type: object
domain.InstResponse:
properties:
data:
@ -1217,6 +1281,47 @@ definitions:
- RoleBranchManager
- RoleCustomer
- RoleCashier
domain.SantimPayCallbackPayload:
properties:
accountNumber:
type: string
address:
type: string
amount:
type: string
created_at:
type: string
currency:
type: string
failureRedirectUrl:
type: string
merId:
type: string
merName:
type: string
message:
type: string
msisdn:
type: string
paymentVia:
type: string
reason:
type: string
receiverWalletID:
type: string
refId:
type: string
status:
type: string
successRedirectUrl:
type: string
thirdPartyId:
type: string
txnId:
type: string
updated_at:
type: string
type: object
domain.ShopBetReq:
properties:
account_name:
@ -1543,6 +1648,13 @@ definitions:
example: 4.22
type: number
type: object
domain.TransactionStatusRequest:
properties:
fullParams:
type: boolean
id:
type: string
type: object
domain.UpcomingEvent:
properties:
away_kit_image:
@ -5200,6 +5312,119 @@ paths:
summary: Get results for an event
tags:
- result
/api/v1/santimpay/b2c-withdrawal:
post:
consumes:
- application/json
description: Initiates a B2C withdrawal request with SantimPay and returns the
response.
parameters:
- description: SantimPay B2C withdrawal request payload
in: body
name: request
required: true
schema:
$ref: '#/definitions/domain.GeneratePaymentURLRequest'
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/domain.Response'
"400":
description: Bad Request
schema:
$ref: '#/definitions/domain.ErrorResponse'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/domain.ErrorResponse'
summary: Process SantimPay B2C Withdrawal
tags:
- SantimPay
/api/v1/santimpay/b2c/partners:
get:
consumes:
- application/json
description: Fetches a list of available B2C payout partners (e.g., Telebirr,
Mpesa, Banks) from SantimPay.
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/domain.Response'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/domain.ErrorResponse'
summary: Get SantimPay B2C Partners
tags:
- SantimPay
/api/v1/santimpay/callback:
post:
consumes:
- application/json
description: Processes a callback from SantimPay, updates transfer status, and
credits user wallet if payment was successful.
parameters:
- description: SantimPay callback payload
in: body
name: request
required: true
schema:
$ref: '#/definitions/domain.SantimPayCallbackPayload'
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/domain.Response'
"400":
description: Bad Request
schema:
$ref: '#/definitions/domain.ErrorResponse'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/domain.ErrorResponse'
summary: Process SantimPay Payment Callback
tags:
- SantimPay
/api/v1/santimpay/direct-payment:
post:
consumes:
- application/json
description: Initiates a direct payment request with SantimPay and returns the
response.
parameters:
- description: SantimPay direct payment request payload
in: body
name: request
required: true
schema:
$ref: '#/definitions/domain.GeneratePaymentURLRequest'
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/domain.Response'
"400":
description: Bad Request
schema:
$ref: '#/definitions/domain.ErrorResponse'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/domain.ErrorResponse'
summary: Process SantimPay Direct Payment
tags:
- SantimPay
/api/v1/santimpay/payment:
post:
consumes:
@ -5211,7 +5436,7 @@ paths:
name: request
required: true
schema:
$ref: '#/definitions/domain.GeneratePaymentURLInput'
$ref: '#/definitions/domain.GeneratePaymentURLRequest'
produces:
- application/json
responses:
@ -5230,6 +5455,36 @@ paths:
summary: Create SantimPay Payment Session
tags:
- SantimPay
/api/v1/santimpay/transaction-status:
post:
consumes:
- application/json
description: Retrieves the real-time status of a transaction from SantimPay.
parameters:
- description: Transaction status request payload
in: body
name: request
required: true
schema:
$ref: '#/definitions/domain.TransactionStatusRequest'
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/domain.Response'
"400":
description: Bad Request
schema:
$ref: '#/definitions/domain.ErrorResponse'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/domain.ErrorResponse'
summary: Check SantimPay Transaction Status
tags:
- SantimPay
/api/v1/search/branch:
get:
consumes:
@ -5936,7 +6191,7 @@ paths:
name: request
required: true
schema:
$ref: '#/definitions/domain.GeneratePaymentURLInput'
$ref: '#/definitions/domain.GeneratePaymentURLRequest'
produces:
- application/json
responses:
@ -6593,6 +6848,42 @@ paths:
summary: Get Veli Gaming Activity
tags:
- Virtual Games - VeliGames
/api/v1/veli/huge-wins:
post:
consumes:
- application/json
description: Retrieves huge win transactions based on brand configuration (e.g.
win > 10000 USD or 100x bet)
parameters:
- description: Huge Wins Request
in: body
name: request
required: true
schema:
$ref: '#/definitions/domain.HugeWinsRequest'
produces:
- application/json
responses:
"200":
description: OK
schema:
allOf:
- $ref: '#/definitions/domain.Response'
- properties:
data:
$ref: '#/definitions/domain.HugeWinsResponse'
type: object
"400":
description: Bad Request
schema:
$ref: '#/definitions/domain.ErrorResponse'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/domain.ErrorResponse'
summary: Get Veli Huge Wins
tags:
- Virtual Games - VeliGames
/api/v1/veli/providers:
post:
consumes:

View File

@ -83,8 +83,11 @@ type ARIFPAYConfig struct {
type SANTIMPAYConfig struct {
SecretKey string `mapstructure:"secret_key"`
MerchantID string `mapstructure:"merchant_id"`
MerchantID string `mapstructure:"merchantId"`
BaseURL string `mapstructure:"base_url"`
NotifyURL string `mapstructure:"notifyUrl"`
CancelUrl string `mapstructure:"cancelUrl"`
SuccessUrl string `mapstructure:"successUrl"`
}
type TELEBIRRConfig struct {
@ -254,7 +257,9 @@ func (c *Config) loadEnv() error {
c.SANTIMPAY.SecretKey = os.Getenv("SANTIMPAY_SECRET_KEY")
c.SANTIMPAY.MerchantID = os.Getenv("SANTIMPAY_MERCHANT_ID")
c.SANTIMPAY.BaseURL = os.Getenv("SANTIMPAY_Base_URL")
c.SANTIMPAY.BaseURL = os.Getenv("SANTIMPAY_BASE_URL")
c.SANTIMPAY.NotifyURL = os.Getenv("SANTIMPAY_NOTIFY_URL")
c.SANTIMPAY.CancelUrl = os.Getenv("SANTIMPAY_CANCEL_URL")
//Alea Play
aleaEnabled := os.Getenv("ALEA_ENABLED")

View File

@ -1,17 +1,13 @@
package domain
type GeneratePaymentURLInput struct {
ID string
Amount int
Reason string
PhoneNumber string
// SuccessRedirectURL string
// FailureRedirectURL string
// CancelRedirectURL string
// NotifyURL string
type GeneratePaymentURLRequest struct {
Amount int `json:"amount"`
Reason string `json:"paymentReason"`
PhoneNumber string `json:"phoneNumber"`
PaymentMethod string `json:"paymentMethod,omitempty"`
}
type InitiatePaymentPayload struct {
type InitiatePaymentRequest struct {
ID string `json:"id"`
Amount int `json:"amount"`
Reason string `json:"paymentReason"`
@ -22,4 +18,64 @@ type InitiatePaymentPayload struct {
NotifyURL string `json:"notifyUrl"`
CancelRedirectURL string `json:"cancelRedirectUrl"`
PhoneNumber string `json:"phoneNumber"`
PaymentMethod string `json:"paymentMethod,omitempty"`
}
type SantimPayCallbackPayload struct {
TxnId string `json:"txnId"`
CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at"`
ThirdPartyId string `json:"thirdPartyId"`
MerId string `json:"merId"`
MerName string `json:"merName"`
Address string `json:"address"`
Amount string `json:"amount"`
Currency string `json:"currency"`
Reason string `json:"reason"`
Msisdn string `json:"msisdn"`
AccountNumber string `json:"accountNumber"`
PaymentVia string `json:"paymentVia"`
RefId string `json:"refId"`
SuccessRedirectUrl string `json:"successRedirectUrl"`
FailureRedirectUrl string `json:"failureRedirectUrl"`
Message string `json:"message"`
Status string `json:"status"`
ReceiverWalletID string `json:"receiverWalletID"`
}
type SantimTokenPayload struct {
Amount int `json:"amount"`
Reason string `json:"paymentReason"`
PaymentMethod string `json:"paymentMethod"`
PhoneNumber string `json:"phoneNumber"`
ID string `json:"id,omitempty"`
}
type Partner struct {
ID string `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
Input string `json:"input"`
}
// B2CPartnersResponse is the top-level response
type B2CPartnersResponse struct {
Partners []Partner `json:"partners"`
}
type SantimpayB2CWithdrawalRequest struct {
ID string `json:"id"`
ClientReference string `json:"clientReference"`
Amount float64 `json:"amount"`
Reason string `json:"reason"`
MerchantID string `json:"merchantId"`
SignedToken string `json:"signedToken"`
ReceiverAccountNumber string `json:"receiverAccountNumber"`
NotifyURL string `json:"notifyUrl"`
PaymentMethod string `json:"paymentMethod"`
}
type TransactionStatusRequest struct {
TransactionID string `json:"id"`
FullParams *bool `json:"fullParams,omitempty"`
}

View File

@ -5,11 +5,12 @@ import (
"time"
"github.com/SamuelTariku/FortuneBet-Backend/internal/config"
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
"github.com/golang-jwt/jwt/v5"
)
type SantimPayClient interface {
GenerateSignedToken(amount int, reason string) (string, error)
GenerateSignedToken(payload domain.SantimTokenPayload) (string, error)
CheckTransactionStatus(id string)
}
@ -23,17 +24,26 @@ func NewSantimPayClient(cfg *config.Config) SantimPayClient {
}
}
func (c *santimClient) GenerateSignedToken(amount int, reason string) (string, error) {
func (c *santimClient) GenerateSignedToken(payload domain.SantimTokenPayload) (string, error) {
now := time.Now().Unix()
claims := jwt.MapClaims{
"amount": amount,
"paymentReason": reason,
"amount": payload.Amount,
"paymentReason": payload.Reason,
"merchantId": c.cfg.SANTIMPAY.MerchantID,
"generated": now,
}
// Optional fields
if payload.PaymentMethod != "" {
claims["paymentMethod"] = payload.PaymentMethod
}
if payload.PhoneNumber != "" {
claims["phoneNumber"] = payload.PhoneNumber
}
token := jwt.NewWithClaims(jwt.SigningMethodES256, claims)
privateKey, err := jwt.ParseECPrivateKeyFromPEM([]byte(c.cfg.SANTIMPAY.SecretKey))
if err != nil {
return "", fmt.Errorf("invalid private key: %w", err)
@ -47,6 +57,7 @@ func (c *santimClient) GenerateSignedToken(amount int, reason string) (string, e
return signedToken, nil
}
func (c *santimClient) CheckTransactionStatus(id string) {
// optional async checker — can log or poll transaction status
fmt.Println("Checking transaction status for:", id)

View File

@ -6,6 +6,8 @@ import (
"encoding/json"
"fmt"
"net/http"
"strconv"
"time"
"github.com/SamuelTariku/FortuneBet-Backend/internal/config"
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
@ -14,42 +16,51 @@ import (
)
// type SantimPayService interface {
// GeneratePaymentURL(input domain.GeneratePaymentURLInput) (map[string]string, error)
// GeneratePaymentURL(req domain.GeneratePaymentURLreq) (map[string]string, error)
// }
type SantimPayService struct {
client SantimPayClient
cfg *config.Config
transferStore wallet.TransferStore
walletSvc *wallet.Service
}
func NewSantimPayService(client SantimPayClient, cfg *config.Config, transferStore wallet.TransferStore) *SantimPayService {
func NewSantimPayService(client SantimPayClient, cfg *config.Config, transferStore wallet.TransferStore, walletSvc *wallet.Service) *SantimPayService {
return &SantimPayService{
client: client,
cfg: cfg,
transferStore: transferStore,
walletSvc: walletSvc,
}
}
func (s *SantimPayService) GeneratePaymentURL(input domain.GeneratePaymentURLInput) (map[string]string, error) {
func (s *SantimPayService) InitiatePayment(req domain.GeneratePaymentURLRequest) (map[string]string, error) {
paymentID := uuid.NewString()
token, err := s.client.GenerateSignedToken(input.Amount, input.Reason)
tokenPayload := domain.SantimTokenPayload{
Amount: req.Amount,
Reason: req.Reason,
}
// 1. Generate signed token (used as Bearer token in headers)
token, err := s.client.GenerateSignedToken(tokenPayload)
if err != nil {
return nil, fmt.Errorf("token generation failed: %w", err)
}
payload := domain.InitiatePaymentPayload{
// 2. Prepare payload (without token in body)
payload := domain.InitiatePaymentRequest{
ID: paymentID,
Amount: input.Amount,
Reason: input.Reason,
Amount: req.Amount,
Reason: req.Reason,
MerchantID: s.cfg.SANTIMPAY.MerchantID,
SuccessRedirectURL: s.cfg.SANTIMPAY.SuccessUrl,
FailureRedirectURL: s.cfg.SANTIMPAY.CancelUrl,
NotifyURL: s.cfg.SANTIMPAY.NotifyURL,
CancelRedirectURL: s.cfg.SANTIMPAY.CancelUrl,
PhoneNumber: req.PhoneNumber,
SignedToken: token,
SuccessRedirectURL: s.cfg.ARIFPAY.SuccessUrl,
FailureRedirectURL: s.cfg.ARIFPAY.ErrorUrl,
NotifyURL: s.cfg.ARIFPAY.B2CNotifyUrl,
CancelRedirectURL: s.cfg.ARIFPAY.CancelUrl,
PhoneNumber: input.PhoneNumber,
}
jsonData, err := json.Marshal(payload)
@ -57,7 +68,16 @@ func (s *SantimPayService) GeneratePaymentURL(input domain.GeneratePaymentURLInp
return nil, fmt.Errorf("failed to marshal payload: %w", err)
}
resp, err := http.Post(s.cfg.SANTIMPAY.BaseURL+"/initiate-payment", "application/json", bytes.NewBuffer(jsonData))
// 3. Prepare request with Bearer token header
httpReq, err := http.NewRequest("POST", s.cfg.SANTIMPAY.BaseURL+"/gateway/initiate-payment", bytes.NewBuffer(jsonData))
if err != nil {
return nil, fmt.Errorf("failed to create HTTP request: %w", err)
}
httpReq.Header.Set("Content-Type", "application/json")
httpReq.Header.Set("Authorization", "Bearer "+token)
client := &http.Client{Timeout: 15 * time.Second}
resp, err := client.Do(httpReq)
if err != nil {
return nil, fmt.Errorf("failed to send HTTP request: %w", err)
}
@ -72,21 +92,324 @@ func (s *SantimPayService) GeneratePaymentURL(input domain.GeneratePaymentURLInp
return nil, fmt.Errorf("failed to decode response: %w", err)
}
// Save transfer
// 4. Save transfer
transfer := domain.CreateTransfer{
Amount: domain.Currency(input.Amount),
Amount: domain.Currency(req.Amount),
Verified: false,
Type: domain.DEPOSIT,
ReferenceNumber: paymentID,
Status: string(domain.PaymentStatusPending),
}
if _, err := s.transferStore.CreateTransfer(context.Background(), transfer); err != nil {
return nil, fmt.Errorf("failed to create transfer: %w", err)
}
// Optionally check transaction status in a goroutine
go s.client.CheckTransactionStatus(paymentID)
// 5. Optionally check transaction status asynchronously
// go s.client.CheckTransactionStatus(paymentID)
return responseBody, nil
}
func (s *SantimPayService) ProcessCallback(ctx context.Context, payload domain.SantimPayCallbackPayload) error {
// 1. Parse amount
amount, err := strconv.ParseFloat(payload.Amount, 64)
if err != nil {
return fmt.Errorf("invalid amount in callback: %w", err)
}
// 2. Retrieve the corresponding transfer by txnId or refId
transfer, err := s.transferStore.GetTransferByReference(ctx, payload.TxnId)
if err != nil {
return fmt.Errorf("failed to fetch transfer for txnId %s: %w", payload.TxnId, err)
}
// 3. Update transfer status based on callback status
switch payload.Status {
case "COMPLETED":
transfer.Status = string(domain.PaymentStatusSuccessful)
transfer.Verified = true
userID, err := strconv.ParseInt(payload.ThirdPartyId, 10, 64)
if err != nil {
return fmt.Errorf("invalid ThirdPartyId '%s': %w", payload.ThirdPartyId, err)
}
wallets, err := s.walletSvc.GetWalletsByUser(ctx, userID)
if err != nil {
return fmt.Errorf("failed to get wallets for user %d: %w", userID, err)
}
// Optionally, credit user wallet
if transfer.Type == domain.DEPOSIT {
if _, err := s.walletSvc.AddToWallet(
ctx,
wallets[0].ID,
domain.Currency(amount),
domain.ValidInt64{},
domain.TRANSFER_SANTIMPAY,
domain.PaymentDetails{
ReferenceNumber: domain.ValidString{
Value: payload.TxnId,
Valid: true,
},
BankNumber: domain.ValidString{},
},
"",
); err != nil {
return fmt.Errorf("failed to credit wallet: %w", err)
}
}
case "FAILED", "CANCELLED":
transfer.Status = string(domain.PaymentStatusFailed)
transfer.Verified = false
default:
// Unknown status
return fmt.Errorf("unknown callback status: %s", payload.Status)
}
// 4. Save the updated transfer
if err := s.transferStore.UpdateTransferStatus(ctx, transfer.ID, string(domain.PaymentStatusCompleted)); err != nil {
return fmt.Errorf("failed to update transfer status: %w", err)
}
if err := s.transferStore.UpdateTransferVerification(ctx, transfer.ID, true); err != nil {
return fmt.Errorf("failed to update transfer verification: %w", err)
}
return nil
}
func (s *SantimPayService) ProcessDirectPayment(ctx context.Context, req domain.GeneratePaymentURLRequest) (map[string]any, error) {
paymentID := uuid.NewString()
tokenPayload := domain.SantimTokenPayload{
Amount: req.Amount,
Reason: req.Reason,
PaymentMethod: req.PaymentMethod,
PhoneNumber: req.PhoneNumber,
}
// 1. Generate signed token for direct payment
token, err := s.client.GenerateSignedToken(tokenPayload)
if err != nil {
return nil, fmt.Errorf("failed to generate signed token: %w", err)
}
// 2. Build payload
payload := domain.InitiatePaymentRequest{
ID: paymentID,
Amount: req.Amount,
Reason: req.Reason,
MerchantID: s.cfg.SANTIMPAY.MerchantID,
SignedToken: token,
PhoneNumber: req.PhoneNumber,
NotifyURL: s.cfg.SANTIMPAY.NotifyURL,
PaymentMethod: req.PaymentMethod,
}
jsonData, err := json.Marshal(payload)
if err != nil {
return nil, fmt.Errorf("failed to marshal payload: %w", err)
}
// 3. Prepare HTTP request
httpReq, err := http.NewRequest("POST", s.cfg.SANTIMPAY.BaseURL+"/direct-payment", bytes.NewBuffer(jsonData))
if err != nil {
return nil, fmt.Errorf("failed to create HTTP request: %w", err)
}
httpReq.Header.Set("Content-Type", "application/json")
httpReq.Header.Set("Authorization", "Bearer "+token)
client := &http.Client{Timeout: 15 * time.Second}
resp, err := client.Do(httpReq)
if err != nil {
return nil, fmt.Errorf("failed to send HTTP request: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("non-200 status code received: %d", resp.StatusCode)
}
// 4. Decode response
var responseBody map[string]any
if err := json.NewDecoder(resp.Body).Decode(&responseBody); err != nil {
return nil, fmt.Errorf("failed to decode response: %w", err)
}
// 5. Save transfer in DB
transfer := domain.CreateTransfer{
Amount: domain.Currency(req.Amount),
Verified: false,
Type: domain.DEPOSIT,
ReferenceNumber: paymentID,
Status: string(domain.PaymentStatusPending),
}
if _, err := s.transferStore.CreateTransfer(context.Background(), transfer); err != nil {
return nil, fmt.Errorf("failed to create transfer: %w", err)
}
// 6. Optionally check transaction status async
// go s.client.CheckTransactionStatus(paymentID)
return responseBody, nil
}
func (s *SantimPayService) GetB2CPartners(ctx context.Context) (*domain.B2CPartnersResponse, error) {
url := fmt.Sprintf("%s/api/v1/gateway/payout/partners", s.cfg.SANTIMPAY.BaseURL)
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil {
return nil, fmt.Errorf("failed to create request: %w", err)
}
HTTPClient := &http.Client{Timeout: 15 * time.Second}
resp, err := HTTPClient.Do(req)
if err != nil {
return nil, fmt.Errorf("failed to call SantimPay API: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("unexpected status code: %d", resp.StatusCode)
}
var partnersResp domain.B2CPartnersResponse
if err := json.NewDecoder(resp.Body).Decode(&partnersResp); err != nil {
return nil, fmt.Errorf("failed to decode response: %w", err)
}
return &partnersResp, nil
}
func (s *SantimPayService) ProcessB2CWithdrawal(ctx context.Context, req domain.GeneratePaymentURLRequest, userId int64) (map[string]any, error) {
transactID := uuid.NewString()
// 1. Generate signed token for B2C
tokenPayload := domain.SantimTokenPayload{
Amount: req.Amount,
Reason: req.Reason,
PaymentMethod: req.PaymentMethod,
PhoneNumber: req.PhoneNumber,
}
signedToken, err := s.client.GenerateSignedToken(tokenPayload)
if err != nil {
return nil, fmt.Errorf("failed to generate signed token for B2C: %w", err)
}
// 2. Build payload
payload := domain.SantimpayB2CWithdrawalRequest{
ID: transactID,
ClientReference: string(rune(userId)),
Amount: float64(req.Amount),
Reason: req.Reason,
MerchantID: s.cfg.SANTIMPAY.MerchantID,
SignedToken: signedToken,
ReceiverAccountNumber: req.PhoneNumber,
NotifyURL: s.cfg.SANTIMPAY.NotifyURL,
PaymentMethod: req.PaymentMethod,
}
jsonData, err := json.Marshal(payload)
if err != nil {
return nil, fmt.Errorf("failed to marshal B2C payload: %w", err)
}
// 3. Send HTTP request
url := s.cfg.SANTIMPAY.BaseURL + "/payout-transfer"
httpReq, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonData))
if err != nil {
return nil, fmt.Errorf("failed to create B2C request: %w", err)
}
httpReq.Header.Set("Content-Type", "application/json")
httpReq.Header.Set("Authorization", "Bearer "+signedToken)
client := &http.Client{Timeout: 15 * time.Second}
resp, err := client.Do(httpReq)
if err != nil {
return nil, fmt.Errorf("failed to send B2C request: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("B2C request failed with status code: %d", resp.StatusCode)
}
// 4. Decode response
var responseBody map[string]any
if err := json.NewDecoder(resp.Body).Decode(&responseBody); err != nil {
return nil, fmt.Errorf("failed to decode B2C response: %w", err)
}
// 5. Persist withdrawal record in DB
withdrawal := domain.CreateTransfer{
Amount: domain.Currency(req.Amount),
Verified: false,
Type: domain.WITHDRAW,
ReferenceNumber: transactID,
Status: string(domain.PaymentStatusPending),
}
if _, err := s.transferStore.CreateTransfer(context.Background(), withdrawal); err != nil {
return nil, fmt.Errorf("failed to create withdrawal transfer: %w", err)
}
return responseBody, nil
}
func (s *SantimPayService) CheckTransactionStatus(ctx context.Context, req domain.TransactionStatusRequest) (map[string]any, error) {
// 1. Generate signed token for status check
tokenPayload := domain.SantimTokenPayload{
ID: req.TransactionID,
}
signedToken, err := s.client.GenerateSignedToken(tokenPayload)
if err != nil {
return nil, fmt.Errorf("failed to generate signed token for transaction status: %w", err)
}
// 2. Build request payload
payload := map[string]any{
"id": req.TransactionID,
"merchantId": s.cfg.SANTIMPAY.MerchantID,
"signedToken": signedToken,
"fullParams": req.FullParams,
"generated": time.Now().Unix(),
}
jsonData, err := json.Marshal(payload)
if err != nil {
return nil, fmt.Errorf("failed to marshal transaction status payload: %w", err)
}
// 3. Send HTTP request
url := s.cfg.SANTIMPAY.BaseURL + "/fetch-transaction-status"
httpReq, err := http.NewRequestWithContext(ctx, "POST", url, bytes.NewBuffer(jsonData))
if err != nil {
return nil, fmt.Errorf("failed to create transaction status request: %w", err)
}
httpReq.Header.Set("Content-Type", "application/json")
httpReq.Header.Set("Authorization", "Bearer "+signedToken)
client := &http.Client{Timeout: 15 * time.Second}
resp, err := client.Do(httpReq)
if err != nil {
return nil, fmt.Errorf("failed to send transaction status request: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("transaction status request failed with status code: %d", resp.StatusCode)
}
// 4. Decode response
var responseBody map[string]any
if err := json.NewDecoder(resp.Body).Decode(&responseBody); err != nil {
return nil, fmt.Errorf("failed to decode transaction status response: %w", err)
}
return responseBody, nil
}

View File

@ -12,13 +12,13 @@ import (
// @Tags SantimPay
// @Accept json
// @Produce json
// @Param request body domain.GeneratePaymentURLInput true "SantimPay payment request payload"
// @Param request body domain.GeneratePaymentURLRequest true "SantimPay payment request payload"
// @Success 200 {object} domain.Response
// @Failure 400 {object} domain.ErrorResponse
// @Failure 500 {object} domain.ErrorResponse
// @Router /api/v1/santimpay/payment [post]
func (h *Handler) CreateSantimPayPaymentHandler(c *fiber.Ctx) error {
var req domain.GeneratePaymentURLInput
func (h *Handler) InititateSantimPayPaymentHandler(c *fiber.Ctx) error {
var req domain.GeneratePaymentURLRequest
if err := c.BodyParser(&req); err != nil {
return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
Error: err.Error(),
@ -26,7 +26,7 @@ func (h *Handler) CreateSantimPayPaymentHandler(c *fiber.Ctx) error {
})
}
paymentURL, err := h.santimpaySvc.GeneratePaymentURL(req)
paymentURL, err := h.santimpaySvc.InitiatePayment(req)
if err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
Error: err.Error(),
@ -41,3 +41,192 @@ func (h *Handler) CreateSantimPayPaymentHandler(c *fiber.Ctx) error {
StatusCode: fiber.StatusOK,
})
}
// ProcessSantimPayCallbackHandler handles incoming SantimPay payment callbacks.
//
// @Summary Process SantimPay Payment Callback
// @Description Processes a callback from SantimPay, updates transfer status, and credits user wallet if payment was successful.
// @Tags SantimPay
// @Accept json
// @Produce json
// @Param request body domain.SantimPayCallbackPayload true "SantimPay callback payload"
// @Success 200 {object} domain.Response
// @Failure 400 {object} domain.ErrorResponse
// @Failure 500 {object} domain.ErrorResponse
// @Router /api/v1/santimpay/callback [post]
func (h *Handler) ProcessSantimPayCallbackHandler(c *fiber.Ctx) error {
var payload domain.SantimPayCallbackPayload
if err := c.BodyParser(&payload); err != nil {
return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
Error: err.Error(),
Message: "Invalid callback payload",
})
}
if err := h.santimpaySvc.ProcessCallback(c.Context(), payload); err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
Error: err.Error(),
Message: "Failed to process SantimPay callback",
})
}
return c.Status(fiber.StatusOK).JSON(domain.Response{
Message: "SantimPay callback processed successfully",
Data: nil,
Success: true,
StatusCode: fiber.StatusOK,
})
}
// ProcessSantimPayDirectPaymentHandler initializes a direct payment session with SantimPay.
//
// @Summary Process SantimPay Direct Payment
// @Description Initiates a direct payment request with SantimPay and returns the response.
// @Tags SantimPay
// @Accept json
// @Produce json
// @Param request body domain.GeneratePaymentURLRequest true "SantimPay direct payment request payload"
// @Success 200 {object} domain.Response
// @Failure 400 {object} domain.ErrorResponse
// @Failure 500 {object} domain.ErrorResponse
// @Router /api/v1/santimpay/direct-payment [post]
func (h *Handler) ProcessSantimPayDirectPaymentHandler(c *fiber.Ctx) error {
var req domain.GeneratePaymentURLRequest
if err := c.BodyParser(&req); err != nil {
return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
Error: err.Error(),
Message: "Invalid direct payment request payload",
})
}
response, err := h.santimpaySvc.ProcessDirectPayment(c.Context(), req)
if err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
Error: err.Error(),
Message: "Failed to process SantimPay direct payment",
})
}
return c.Status(fiber.StatusOK).JSON(domain.Response{
Message: "SantimPay direct payment processed successfully",
Data: response,
Success: true,
StatusCode: fiber.StatusOK,
})
}
// GetSantimPayB2CPartnersHandler retrieves all available SantimPay B2C payout partners.
//
// @Summary Get SantimPay B2C Partners
// @Description Fetches a list of available B2C payout partners (e.g., Telebirr, Mpesa, Banks) from SantimPay.
// @Tags SantimPay
// @Accept json
// @Produce json
// @Success 200 {object} domain.Response
// @Failure 500 {object} domain.ErrorResponse
// @Router /api/v1/santimpay/b2c/partners [get]
func (h *Handler) GetSantimPayB2CPartnersHandler(c *fiber.Ctx) error {
partners, err := h.santimpaySvc.GetB2CPartners(c.Context())
if err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
Error: err.Error(),
Message: "Failed to fetch SantimPay B2C partners",
})
}
return c.Status(fiber.StatusOK).JSON(domain.Response{
Message: "SantimPay B2C partners retrieved successfully",
Data: partners,
Success: true,
StatusCode: fiber.StatusOK,
})
}
// ProcessSantimPayB2CWithdrawalHandler processes a B2C (Withdrawal) transaction with SantimPay.
//
// @Summary Process SantimPay B2C Withdrawal
// @Description Initiates a B2C withdrawal request with SantimPay and returns the response.
// @Tags SantimPay
// @Accept json
// @Produce json
// @Param request body domain.GeneratePaymentURLRequest true "SantimPay B2C withdrawal request payload"
// @Success 200 {object} domain.Response
// @Failure 400 {object} domain.ErrorResponse
// @Failure 500 {object} domain.ErrorResponse
// @Router /api/v1/santimpay/b2c-withdrawal [post]
func (h *Handler) ProcessSantimPayB2CWithdrawalHandler(c *fiber.Ctx) error {
var req domain.GeneratePaymentURLRequest
if err := c.BodyParser(&req); err != nil {
return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
Error: err.Error(),
Message: "Invalid B2C withdrawal request payload",
})
}
// Extract userId from context/session (adapt based on your auth flow)
userId, ok := c.Locals("userId").(int64)
if !ok {
return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
Error: "missing userId in context",
Message: "Could not process withdrawal without user ID",
})
}
response, err := h.santimpaySvc.ProcessB2CWithdrawal(c.Context(), req, userId)
if err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
Error: err.Error(),
Message: "Failed to process SantimPay B2C withdrawal",
})
}
return c.Status(fiber.StatusOK).JSON(domain.Response{
Message: "SantimPay B2C withdrawal processed successfully",
Data: response,
Success: true,
StatusCode: fiber.StatusOK,
})
}
// CheckSantimPayTransactionStatusHandler checks the status of a SantimPay transaction.
//
// @Summary Check SantimPay Transaction Status
// @Description Retrieves the real-time status of a transaction from SantimPay.
// @Tags SantimPay
// @Accept json
// @Produce json
// @Param request body domain.TransactionStatusRequest true "Transaction status request payload"
// @Success 200 {object} domain.Response
// @Failure 400 {object} domain.ErrorResponse
// @Failure 500 {object} domain.ErrorResponse
// @Router /api/v1/santimpay/transaction-status [post]
func (h *Handler) CheckSantimPayTransactionStatusHandler(c *fiber.Ctx) error {
var req domain.TransactionStatusRequest
if err := c.BodyParser(&req); err != nil {
return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
Error: err.Error(),
Message: "Invalid transaction status request payload",
})
}
// Optional: extract fullParams from request, default to true if not provided
fullParams := true
if req.FullParams == nil {
req.FullParams = &fullParams
}
response, err := h.santimpaySvc.CheckTransactionStatus(c.Context(), req)
if err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
Error: err.Error(),
Message: "Failed to check SantimPay transaction status",
})
}
return c.Status(fiber.StatusOK).JSON(domain.Response{
Message: "SantimPay transaction status retrieved successfully",
Data: response,
Success: true,
StatusCode: fiber.StatusOK,
})
}

View File

@ -14,7 +14,7 @@ import (
// @Tags Telebirr
// @Accept json
// @Produce json
// @Param request body domain.GeneratePaymentURLInput true "Telebirr payment request payload"
// @Param request body domain.GeneratePaymentURLRequest true "Telebirr payment request payload"
// @Success 200 {object} domain.Response
// @Failure 400 {object} domain.ErrorResponse
// @Failure 500 {object} domain.ErrorResponse

View File

@ -127,7 +127,13 @@ func (a *App) initAppRoutes() {
groupV1.Post("/telebirr/callback", h.HandleTelebirrCallback)
//Santimpay
groupV1.Post("/santimpay/init-payment", h.CreateSantimPayPaymentHandler)
groupV1.Post("/santimpay/init-payment", a.authMiddleware, h.InititateSantimPayPaymentHandler)
groupV1.Post("/santimpay/callback", h.ProcessSantimPayCallbackHandler)
groupV1.Post("/santimpay/direct-payment", a.authMiddleware, h.ProcessSantimPayDirectPaymentHandler)
groupV1.Get("/santimpay/b2c/partners", h.GetSantimPayB2CPartnersHandler)
groupV1.Post("/santimpay/b2c/withdraw", a.authMiddleware, h.ProcessSantimPayB2CWithdrawalHandler)
groupV1.Post("/santimpay/transaction/verify", a.authMiddleware, h.CheckSantimPayTransactionStatusHandler)
// groupV1.Post("/arifpay/b2c/transfer", a.authMiddleware, h.B2CTransferHandler)
// groupV1.Post("/arifpay/transaction-id/verify-transaction", a.authMiddleware, h.ArifpayVerifyByTransactionIDHandler)
// groupV1.Get("/arifpay/session-id/verify-transaction/:session_id", a.authMiddleware, h.ArifpayVerifyBySessionIDHandler)