Skip to content

Wave Money ​

Wave Money's payment gateway sends the customer to a Wave payment page to pay with their WavePay wallet.

CallWhat it doesReturns
wave.Initiate(ctx, &data)Redirect to Wave's payment page*RedirectPayment
wave.HandleCallback(request)Verify the callback*PaymentCallback

Wave Money has no status API in this package: the callback is the only payment result.

Responses shows what Wave Money puts in each result.

How it works ​

Wave sends the customer back to your return URL and posts the result to your callback URL separately.

Wave Money: payment request, authenticate, resultCustomerYour appWave MoneyCheck outPayment request with hashwave.Initiate(ctx, &data)transaction_idRedirect to authenticate/authenticate?transaction_id=…Pay with WavePayBack to the frontend URLreturnUrl: not proof of paymentBackend result URL callbackPOST to callbackUrl, hashValueVerified callback is proofwave.HandleCallback(request)
Wave Money: payment request, authenticate, result

Initiating a Payment ​

go
import (
	"fmt"
	"net/http"

	myanmarpayments "github.com/laranex/go-myanmar-payments/v4"
	"github.com/laranex/go-myanmar-payments/v4/wavemoney"
)

wave, err := wavemoney.New(wavemoney.Config{
	MerchantID:        "...",
	SecretKey:         "...",
	MerchantName:      "My Shop",
	TimeToLiveSeconds: 300,
	TimeoutSeconds:    30,
}, nil)
if err != nil {
	return err
}

data := wavemoney.PaymentData{
	OrderID:     fmt.Sprintf("ORDER_%d", order.ID),
	CallbackURL: "https://shop.test/payments/wave/callback",
	ReturnURL:   fmt.Sprintf("https://shop.test/orders/%d", order.ID),
	Description: fmt.Sprintf("Order #%d", order.ID),
	Items: []wavemoney.Item{
		{Name: "Product A", Amount: myanmarpayments.Kyat(6000)},
		{Name: "Product B", Amount: myanmarpayments.Kyat(4000)},
	},
}

payment, err := wave.Initiate(ctx, &data)
if err != nil {
	return err
}

// Store data.MerchantReferenceID with the order: Initiate filled it in.

http.Redirect(w, r, payment.URL, http.StatusFound)

wavemoney.PaymentData ​

FieldTypeRequiredRules
OrderIDstringYesYour order ID. One order can have several payment attempts
CallbackURLstringYesAbsolute http or https URL that Wave posts the result to. Wave may require HTTPS with a CA-issued certificate in production
ReturnURLstringYesAbsolute http or https URL Wave sends the customer back to. Not proof of payment
DescriptionstringYesShown to the customer
Items[]wavemoney.ItemYesAt least one item
Amountmyanmarpayments.AmountNoWhole kyat, greater than 0 (Wave doesn't accept decimals). Unset charges the sum of the items. Wave only accepts MMK
MerchantReferenceIDstringNoUnique ID of this attempt. Empty means a random ID

wavemoney.Item has a Name and an Amount in whole kyat, greater than 0. The items are summed with exact integer arithmetic, never floats; data.ResolvedAmount() returns the total that will be charged. Item names are sent as written: <, > and & are not escaped in the items JSON.

Merchant Reference ID ​

Wave rejects a reused merchant_reference_id (409 Record already exists), so every attempt, including a retry of the same order, needs a new one. Leave it empty to get a fresh random ID, and store it: Wave marks orderId as optional in callbacks, while merchantReferenceId is always present. Initiate takes a pointer and writes the generated ID to data.MerchantReferenceID once data passes validation.

Handling Callbacks ​

go
import (
	"net/http"

	myanmarpayments "github.com/laranex/go-myanmar-payments/v4"
)

// POST /payments/wave/callback
request, err := myanmarpayments.NewCallbackRequestFromHTTP(r)
if err != nil {
	http.Error(w, "invalid callback", http.StatusBadRequest)
	return
}
callback, err := wave.HandleCallback(request)
if err != nil {
	http.Error(w, "invalid callback", http.StatusBadRequest)
	return
}

if callback.IsSuccessful() {
	// callback.OrderID is your OrderID
	// callback.Raw["merchantReferenceId"] is the attempt's reference
	// callback.GatewayReference is Wave's transactionId
}

callback.Acknowledgement.Write(w)

callback.OrderID falls back to merchantReferenceId when Wave's orderId is missing, null or empty.

Responses ​

What Wave Money puts in each field. See Results and PaymentCallback & Status for the full structs. On error the result is nil. A field the gateway didn't send is "". Raw holds plain Go values (JSON numbers become json.Numbers), while the typed fields such as Amount keep the exact text Wave sent.

Initiate() → *myanmarpayments.RedirectPayment ​

Field / MethodWave Money value
Flow()FlowRedirect
OrderIDYour data.OrderID
URL{AuthenticateURL}/authenticate?transaction_id=… (URL-encoded), e.g. https://payments.wavemoney.io/authenticate?transaction_id=…
GatewayReferenceWave transaction_id. Always set
RawWave's /payment response: message (success), transaction_id

The attempt's MerchantReferenceID is not on the result: read it from data.MerchantReferenceID.

HandleCallback() → *myanmarpayments.PaymentCallback ​

FieldWave Money value
OrderIDWave orderId, falling back to merchantReferenceId when it is missing, null or empty
Statusstatus mapped, see Statuses
GatewayStatusWave status, trimmed, e.g. PAYMENT_CONFIRMED
GatewayReferenceWave transactionId
AmountWave amount, e.g. 10000
RawThe verified body: status, merchantId, orderId, merchantReferenceId, frontendResultUrl, backendResultUrl, initiatorMsisdn, amount, timeToLiveSeconds, paymentDescription, currency, additionalField1–5, transactionId, paymentRequestId, requestTime, hashValue
AcknowledgementHTTP 200, empty body, Content-Type: text/plain

Statuses ​

Only PAYMENT_CONFIRMED means the customer paid.

Wave statusPaymentStatus
PAYMENT_CONFIRMEDStatusSuccessful
INSUFFICIENT_BALANCEStatusPending
ACCOUNT_LOCKED, BILL_COLLECTION_FAILEDStatusFailed
PAYMENT_REQUEST_CANCELLEDStatusCanceled
TRANSACTION_TIMED_OUT, SCHEDULER_TRANSACTION_TIMED_OUTStatusExpired
anything elseStatusUnknown

SCHEDULER_TRANSACTION_TIMED_OUT arrives up to 15 minutes after the time-to-live ends.

Errors ​

CallReturnsWhen
Initiate()*InvalidPaymentDataErrordata.Validate() fails. Nothing is sent and data is left untouched. Item errors use items.0.amount keys
Initiate()*APIErrorWave answers with an HTTP error, a message other than success, or no transaction_id
HandleCallback()*SignatureVerificationErrorhashValue doesn't match, or a hashed field holds an object or array

The error types live in the root myanmarpayments package. HTTPStatus tells Wave's rejections apart: 400 invalid hash, 404 unknown merchant, 409 reused reference, 422 validation (GatewayCode is VALIDATION_ERROR). When Wave can't be reached or ctx is canceled, Initiate returns *APIError, which unwraps to the cause (errors.Is(err, context.DeadlineExceeded)).

Released under the MIT License, except where a package says otherwise.