Documentation
¶
Index ¶
- Constants
- Variables
- func IsFundsRelatedCode(code string) bool
- func Sign(body []byte, secret string) string
- func ValidCurrency(code string) bool
- func Verify(body []byte, signature, secret string) bool
- type APIError
- type BalanceRequest
- type BalanceResult
- type BetLevel
- type BetLevelData
- type BetLevelsRequest
- type BetWinRequest
- type BetWinResult
- type BetWinTransaction
- type BetWinTransactionResult
- type Client
- func (c *Client) BetLevels(ctx context.Context, req BetLevelsRequest) ([]BetLevelData, error)
- func (c *Client) CancelFreespins(ctx context.Context, req FreespinsCancelRequest) error
- func (c *Client) IssueFreespins(ctx context.Context, req FreespinsIssueRequest) error
- func (c *Client) LaunchDemo(ctx context.Context, req LaunchDemoRequest) (LaunchResult, error)
- func (c *Client) LaunchReal(ctx context.Context, req LaunchRealRequest) (LaunchResult, error)
- func (c *Client) ListGames(ctx context.Context, q ListGamesQuery) ([]GameInfo, error)
- type ClientOption
- type CurrencyBetLevels
- type DispatchResponse
- type ErrorEnvelope
- type ErrorMeta
- type FinishRequest
- type FinishResult
- type FreespinsCancelRequest
- type FreespinsFinishHandler
- type FreespinsFinishRequest
- type FreespinsFinishResult
- type FreespinsIssueRequest
- type GameImage
- type GameInfo
- type GameRestrictionRule
- type GameRestrictions
- type LaunchAccount
- type LaunchDemoRequest
- type LaunchRealRequest
- type LaunchResult
- type LaunchURLs
- type ListGamesQuery
- type Money
- func (m Money) Add(other Money) Money
- func (m Money) ClampToZero() Money
- func (m Money) Cmp(other Money) int
- func (m Money) Equal(other Money) bool
- func (m Money) Format(scale int) string
- func (m Money) FormatBonus() string
- func (m Money) IsNegative() bool
- func (m Money) IsPositive() bool
- func (m Money) IsZero() bool
- func (m Money) MarshalJSON() ([]byte, error)
- func (m Money) MinorUnits() *big.Int
- func (m Money) Scale() int
- func (m Money) String() string
- func (m Money) Sub(other Money) Money
- func (m *Money) UnmarshalJSON(b []byte) error
- type RequestContext
- type RollbackRequest
- type RollbackResult
- type RollbackTransaction
- type RollbackTransactionResult
- type Route
- type TransactionType
- type WalletError
- func ErrAlreadyRolledBack() *WalletError
- func ErrBadRequest(message string) *WalletError
- func ErrBetLimitReached(balance Money) *WalletError
- func ErrCurrencyNotAllowed(message string) *WalletError
- func ErrFreespinsStateTransition(message string) *WalletError
- func ErrGameForbidden(message string) *WalletError
- func ErrInsufficientFunds(balance Money) *WalletError
- func ErrInternal(message string) *WalletError
- func ErrInvalidFreespinsIssue(message string) *WalletError
- func ErrInvalidPlayer(message string) *WalletError
- func ErrInvalidSignature() *WalletError
- func ErrMaxBetExceeded(balance Money) *WalletError
- func ErrNotFound(message string) *WalletError
- func ErrPlayerDisabled(message string) *WalletError
- func ErrServiceUnavailable(message string) *WalletError
- func WalletErrorWithAPICode(apiCode, message string, balance *Money) (*WalletError, error)
- type WalletHandler
- type WalletServer
- type WalletServerOption
Constants ¶
const ( APICodeInsufficientFunds = "100" APICodeInvalidPlayer = "101" APICodeBetLimitReached = "105" APICodeMaxBetExceeded = "106" APICodeGameForbidden = "107" APICodePlayerDisabled = "110" APICodeCountryRestricted = "153" APICodeCurrencyNotAllowed = "154" APICodeFieldImmutable = "155" APICodeBadRequest = "400" APICodeForbidden = "403" APICodeNotFound = "404" // APICodeAlreadyRolledBack is a Beexar extension. Return it from /betwin to // reject a transaction whose id_provider was already rolled back — // including a rollback that arrived BEFORE the bet it reverses. APICodeAlreadyRolledBack = "409" APICodeGameNotAvailable = "405" APICodeCasinoDisabled = "410" APICodeUnknownError = "500" APICodeRequestTimeout = "504" )
SoftSwiss API codes, ported verbatim from the platform's own registry.
Two things about this contract surprise everyone once:
- There are only two HTTP statuses (400 and 500) and two Twirp codes ("invalid_argument" and "internal"). All meaning lives in meta.api_code. Branch on that, never on the status.
- A signature failure is HTTP 400 with api_code "403", not HTTP 403. Answering 403 breaks the contract.
const ( APICodeFreespinsNotAvailable = "406" // This feature is not enabled for your casino. APICodeFreespinsImpossible = "601" // Impossible to issue freespins in requested game. APICodeFreespinsNoGame = "602" // You shall provide at least one game to issue freespins. APICodeFreespinsBadExpiration = "603" // Bad expiration date. APICodeFreespinsStateTransition = "605" // Can't change issue state from its current to requested. APICodeFeatureNotSupported = "608" // Feature is not supported for the requested game. APICodeFreespinsInvalidIssue = "610" // Invalid freespins issue. APICodeFreespinsExpired = "611" // Freespins issue has already expired. APICodeFreespinsCantCancel = "620" // Freespins issue can't be canceled. )
Freespins. Beexar answers your IssueFreespins / CancelFreespins with 405, 406 and the 6xx codes; your /freespins/finish answers 610 for an issue you never issued and 605 for one you canceled.
const ( TwirpCodeInvalidArgument = "invalid_argument" TwirpCodeInternal = "internal" )
Twirp codes. Only these two exist on this contract.
const ( // MaxScale bounds fractional digits, mirroring the platform's // NUMERIC(38,16) storage. MaxScale = 16 // MaxClientDecimalLen mirrors the `maxLength: 40` the OpenAPI contract puts // on amount fields. MaxClientDecimalLen = 40 )
const CurrencyPattern = `^[A-Z][A-Z0-9_]{2,31}$`
CurrencyPattern is the regular expression currencies must match.
const MaxBodyBytes = 64 * 1024
MaxBodyBytes mirrors the gateway's own cap on an operator request body, so a body it would never have sent cannot make your process allocate.
const ProductionBaseURL = "https://gateway.beexar.com"
ProductionBaseURL is the Beexar gateway.
const SignatureHeader = "X-REQUEST-SIGN"
SignatureHeader is the header carrying the request signature: hex-encoded HMAC-SHA256 of the RAW request body, keyed with the operator's API secret. 64 lowercase hex characters.
const Version = "1.2.0"
Version is stamped at release time from sdk/VERSION. The same number is published for the Node, PHP, Go and Python SDKs and always means the same contract snapshot.
Variables ¶
var ErrMalformedAmount = errors.New("beexar: not a valid decimal amount")
ErrMalformedAmount is returned for a string outside the contract's amount shape.
var Routes = []Route{RouteBalance, RouteBetWin, RouteRollback, RouteFinish, RouteFreespinsFinish}
Routes lists every callback route.
var ZeroMoney = Money{/* contains filtered or unexported fields */}
ZeroMoney is zero at scale 2 — the scale the contract's examples use.
Functions ¶
func IsFundsRelatedCode ¶
IsFundsRelatedCode reports whether an api_code must carry the player's balance in meta.balance.
The platform will not complain if you omit it — it reads the field with the error swallowed — so the player simply sees a wrong balance. That is exactly why this SDK refuses to build such an error without one.
func ValidCurrency ¶
ValidCurrency reports whether code matches the contract's currency pattern: ISO-4217 fiat, a crypto ticker, or an operator's CUSTOM_ code.
Lowercase is REJECTED, never normalised — the platform's schema does the same, and quietly upcasing here would hide an operator bug until settlement.
Types ¶
type APIError ¶
APIError is an error answer from the Beexar gateway to one of YOUR calls (LaunchReal, LaunchDemo, ListGames, IssueFreespins, CancelFreespins, BetLevels).
func (*APIError) Retryable ¶
Retryable reports whether the call is worth repeating. A 400 on this contract is terminal — retrying it changes nothing.
func (*APIError) SignatureError ¶
SignatureError reports whether the gateway rejected the request signature. Keyed on api_code, because the status alone is ambiguous.
type BalanceRequest ¶
type BalanceRequest struct {
AccountID string `json:"account_id"`
Currency string `json:"currency"`
GameID string `json:"game_id"`
SessionID string `json:"session_id,omitempty"`
}
BalanceRequest asks for a player's current balance.
type BalanceResult ¶
type BalanceResult struct {
Balance Money
}
BalanceResult answers a BalanceRequest.
type BetLevel ¶ added in v1.2.0
type BetLevel struct {
BetLevel int64 `json:"bet_level"`
// Amount is the TOTAL bet of one round, a decimal string in the currency's
// main unit — ParseMoney(level.Amount) for arithmetic.
Amount string `json:"amount"`
}
BetLevel is what one bet level costs per round in one currency.
type BetLevelData ¶ added in v1.2.0
type BetLevelData struct {
Games []string `json:"games"`
Currencies []CurrencyBetLevels `json:"currencies"`
}
BetLevelData is the games that share the same bet levels in every currency. Each game appears once.
type BetLevelsRequest ¶ added in v1.2.0
type BetLevelsRequest struct {
// Games are game slugs.
Games []string `json:"games,omitempty"`
// Currencies are currency codes.
Currencies []string `json:"currencies,omitempty"`
}
BetLevelsRequest filters Bet/Levels. Unknown values are ignored; leave one empty for all of them.
type BetWinRequest ¶
type BetWinRequest struct {
AccountID string
Currency string
GameID string
RoundID string
Finished bool
// Transactions arrive in request order. Apply them in this order, atomically.
Transactions []BetWinTransaction
SessionID string
}
BetWinRequest debits and/or credits a player.
type BetWinResult ¶
type BetWinResult struct {
// RoundID is YOUR round id.
RoundID string
// Balance after all transactions in this request.
Balance Money
// Transactions holds one entry per request transaction, in the same order.
Transactions []BetWinTransactionResult
}
BetWinResult answers a BetWinRequest.
type BetWinTransaction ¶
type BetWinTransaction struct {
IDProvider string
Type TransactionType
// Amount is always greater than zero — the SDK rejects 0 before your code
// runs.
Amount Money
}
BetWinTransaction is one money movement inside a /betwin request.
type BetWinTransactionResult ¶
type BetWinTransactionResult struct {
// IDProvider echoes the platform's id back unchanged.
IDProvider string
// ID is YOUR transaction id. On a replay, return the one you stored the
// first time.
ID string
// BonusAmount is the bonus balance moved by this transaction. The contract
// requires the field; the SDK emits "0.00" when you leave it nil.
BonusAmount *Money
}
BetWinTransactionResult is your answer for one transaction.
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client makes the calls you send to Beexar: launch a session, list the catalogue, issue and cancel freespins, read the bet levels they are issued at.
Every POST is signed with hex(HMAC-SHA256(body, apiSecret)) over the exact bytes that go on the wire — the body is marshalled once, signed, and sent. Marshalling twice is how signatures mysteriously stop matching.
func NewClient ¶
func NewClient(casinoID, apiSecret string, opts ...ClientOption) (*Client, error)
NewClient builds a client. casinoID is your operator slug; apiSecret comes from the backoffice and must never reach a browser.
func (*Client) BetLevels ¶ added in v1.2.0
func (c *Client) BetLevels(ctx context.Context, req BetLevelsRequest) ([]BetLevelData, error)
BetLevels is Bet/Levels: the bet levels freespins can be issued at, per game and currency. BetLevel in IssueFreespins is one of these.
func (*Client) CancelFreespins ¶ added in v1.2.0
func (c *Client) CancelFreespins(ctx context.Context, req FreespinsCancelRequest) error
CancelFreespins is Freespins/Cancel, at any time — even mid-play. A canceled issue never reaches /freespins/finish: what it won is forfeited. Canceling an issue that has already been played out is refused with api_code 620.
func (*Client) IssueFreespins ¶ added in v1.2.0
func (c *Client) IssueFreespins(ctx context.Context, req FreespinsIssueRequest) error
IssueFreespins is Freespins/Issue: it gives a player free rounds in the listed games. The player finds them on their next LaunchReal of one of those games; what they win arrives once, on your /freespins/finish callback.
Record the issue on your side before calling this — the result names only issue_id. A repeat with the same terms answers like the first call, so retry a timeout or a 5xx with the SAME request: a ValidUntil computed again is other terms, and other terms under one IssueID answer 610.
func (*Client) LaunchDemo ¶
func (c *Client) LaunchDemo(ctx context.Context, req LaunchDemoRequest) (LaunchResult, error)
LaunchDemo starts a demo session on a virtual balance. No wallet callbacks are made during demo play.
func (*Client) LaunchReal ¶
func (c *Client) LaunchReal(ctx context.Context, req LaunchRealRequest) (LaunchResult, error)
LaunchReal starts a real-money session and returns the URL to embed.
type ClientOption ¶
type ClientOption func(*Client)
ClientOption configures a Client.
func WithBaseURL ¶
func WithBaseURL(u string) ClientOption
WithBaseURL points the client at a different gateway (staging, or a local stack).
func WithHTTPClient ¶
func WithHTTPClient(h *http.Client) ClientOption
WithHTTPClient supplies the HTTP client to use, for timeouts, proxies or tests.
type CurrencyBetLevels ¶ added in v1.2.0
type CurrencyBetLevels struct {
Currency string `json:"currency"`
BetLevels []BetLevel `json:"bet_levels"`
}
CurrencyBetLevels is one currency's bet levels: 1..N without gaps, amounts strictly ascending.
type DispatchResponse ¶
DispatchResponse is what the transport should write back.
type ErrorEnvelope ¶
type ErrorEnvelope struct {
Code string `json:"code"`
Msg string `json:"msg"`
Meta ErrorMeta `json:"meta"`
}
ErrorEnvelope is the error body both sides of this contract exchange.
type ErrorMeta ¶
type ErrorMeta struct {
APICode string `json:"api_code"`
APIMessage string `json:"api_message"`
Balance string `json:"balance,omitempty"`
}
ErrorMeta is the meta object of the Twirp error envelope.
type FinishRequest ¶
FinishRequest closes a round. It carries no game_id.
type FinishResult ¶
type FinishResult struct {
Balance Money
}
FinishResult answers a FinishRequest.
type FreespinsCancelRequest ¶ added in v1.2.0
type FreespinsCancelRequest struct {
// IssueID is the issue you issued, or one you are about to: a cancel can
// come first.
IssueID string `json:"issue_id"`
}
FreespinsCancelRequest is Freespins/Cancel. The client adds casino_id.
type FreespinsFinishHandler ¶ added in v1.2.0
type FreespinsFinishHandler interface {
FreespinsFinish(ctx context.Context, req FreespinsFinishRequest, rc RequestContext) (FreespinsFinishResult, error)
}
FreespinsFinishHandler is the optional fifth method, for a wallet that issues freespins: /freespins/finish. Implement it on the same value you pass to NewWalletServer; a handler without it answers that route the way an unknown one is answered (api_code 404).
Credit Amount to the player of the issue once per IssueID. The platform sends the result once and retries the same body until it gets a 200, so a repeat must answer the balance you stored the first time and credit nothing.
type FreespinsFinishRequest ¶ added in v1.2.0
type FreespinsFinishRequest struct {
// IssueID is YOUR id for the issue — the IssueID you sent to
// Client.IssueFreespins.
IssueID string
// Amount is the total won, in the issue's currency at its scale. Zero or
// more — zero ("0.00" in EUR, "0" in JPY) is a valid result, not an error:
// the freespins were played and won nothing.
Amount Money
}
FreespinsFinishRequest is the result of a freespins issue: what its freespins won in total, for you to credit to the player you issued them to.
type FreespinsFinishResult ¶ added in v1.2.0
type FreespinsFinishResult struct {
// Balance after the credit. On a repeat, the one you returned the first
// time.
Balance Money
}
FreespinsFinishResult answers a FreespinsFinishRequest.
type FreespinsIssueRequest ¶ added in v1.2.0
type FreespinsIssueRequest struct {
// IssueID is YOUR id for this issue, at most 255 characters. Repeating it
// with the same terms answers like the first call; with other terms it is
// refused (610).
IssueID string `json:"issue_id"`
// Games are game slugs. The quantity is shared by all of them, not given
// in each.
Games []string `json:"games"`
// FreespinsQuantity is at most 10000.
FreespinsQuantity int64 `json:"freespins_quantity"`
// BetLevel is a level from Client.BetLevels — the total bet of every free
// round.
BetLevel int64 `json:"bet_level"`
// ValidUntil is when the freespins stop being playable, at most one month
// ahead, in UTC with a Z: time.Now().UTC().Add(7*24*time.Hour).Format(time.RFC3339).
ValidUntil string `json:"valid_until"`
// Account is the player, as in a launch. ID and Currency decide whose
// freespins these are.
Account LaunchAccount `json:"account"`
}
FreespinsIssueRequest is Freespins/Issue: FreespinsQuantity free rounds for one player, at one bet level, in one or more games. The client adds casino_id.
Record the issue in your own ledger BEFORE sending it: the result arrives later on /freespins/finish carrying only issue_id and amount, so your record is what says whose wallet, in which currency, to credit.
type GameImage ¶ added in v1.1.0
type GameImage struct {
Type string `json:"type,omitempty"`
Width int `json:"width,omitempty"`
Height int `json:"height,omitempty"`
URL string `json:"url,omitempty"`
WebPURL string `json:"webp_url,omitempty"`
}
GameImage is one catalog image of a game: a PNG, with a WebP copy when available.
type GameInfo ¶
type GameInfo struct {
Title string `json:"title,omitempty"`
Identifier string `json:"identifier,omitempty"`
Category string `json:"category,omitempty"`
FeatureGroup string `json:"feature_group,omitempty"`
Payout float64 `json:"payout,omitempty"`
VolatilityRating string `json:"volatility_rating,omitempty"`
HasFreespins bool `json:"has_freespins,omitempty"`
BonusBuy bool `json:"bonus_buy,omitempty"`
DemoAvailable bool `json:"demo_available,omitempty"`
Thumbnail string `json:"thumbnail,omitempty"`
Currencies []string `json:"currencies,omitempty"`
// Restrictions carries the country blacklist that applies to this game.
Restrictions *GameRestrictions `json:"restrictions,omitempty"`
// Images are every catalog image the game has, one per format, the
// square tile first: square_640 (640×640), portrait_3x4 (600×800),
// landscape_16x9 (1280×720), social_1200x630 (1200×630 link-preview
// card). Empty when the game has none — Thumbnail then carries the
// platform default.
Images []GameImage `json:"images,omitempty"`
}
GameInfo describes one game in the operator catalogue.
type GameRestrictionRule ¶
type GameRestrictionRule struct {
// Blacklist holds ISO country codes the game must not be offered in.
Blacklist []string `json:"blacklist,omitempty"`
}
GameRestrictionRule is one set of country restrictions.
type GameRestrictions ¶
type GameRestrictions struct {
Default *GameRestrictionRule `json:"default,omitempty"`
}
GameRestrictions groups the per-game availability rules.
type LaunchAccount ¶
type LaunchAccount struct {
ID string `json:"id"`
Currency string `json:"currency"`
Firstname string `json:"firstname,omitempty"`
Lastname string `json:"lastname,omitempty"`
Nickname string `json:"nickname,omitempty"`
Email string `json:"email,omitempty"`
Country string `json:"country,omitempty"`
DateOfBirth string `json:"date_of_birth,omitempty"`
RegisteredAt string `json:"registered_at,omitempty"`
Tags []string `json:"tags,omitempty"`
}
LaunchAccount identifies the player a real-money session is launched for.
type LaunchDemoRequest ¶
type LaunchDemoRequest struct {
Game string `json:"game"`
Currency string `json:"currency,omitempty"`
Balance string `json:"balance,omitempty"`
PlayerID string `json:"player_id,omitempty"`
Locale string `json:"locale,omitempty"`
IP string `json:"ip,omitempty"`
ClientType string `json:"client_type,omitempty"`
URLs *LaunchURLs `json:"urls,omitempty"`
Jurisdiction string `json:"jurisdiction,omitempty"`
}
LaunchDemoRequest launches a demo session on a virtual balance.
type LaunchRealRequest ¶
type LaunchRealRequest struct {
Game string `json:"game"`
Account LaunchAccount `json:"account"`
Locale string `json:"locale,omitempty"`
IP string `json:"ip,omitempty"`
ClientType string `json:"client_type,omitempty"`
URLs *LaunchURLs `json:"urls,omitempty"`
Jurisdiction string `json:"jurisdiction,omitempty"`
SessionID string `json:"session_id,omitempty"`
SessionPayload string `json:"session_payload,omitempty"`
}
LaunchRealRequest launches a real-money session.
type LaunchResult ¶
type LaunchResult struct {
LaunchURL string `json:"launch_url"`
}
LaunchResult carries the URL to embed.
type LaunchURLs ¶
type LaunchURLs struct {
ReturnURL string `json:"return_url,omitempty"`
DepositURL string `json:"deposit_url,omitempty"`
}
LaunchURLs are the pages the game links back to.
type ListGamesQuery ¶
type ListGamesQuery struct {
// Operator defaults to the client's CasinoID.
Operator string
// Active, when set, filters by whether the game is enabled.
Active *bool
}
ListGamesQuery filters the catalogue.
type Money ¶
type Money struct {
// contains filtered or unexported fields
}
Money is a decimal amount in a currency's main unit — "0.90" is ninety cents.
There is no constructor taking a float. IEEE-754 cannot hold 0.1, and a wallet that rounds a player's balance by a hundredth of a cent per round is a reconciliation incident rather than a rounding detail.
The zero value is a valid zero amount at scale 0.
func MoneyFromMinorUnits ¶
MoneyFromMinorUnits builds an amount from an integer number of minor units — the bridge for a ledger that stores cents. MoneyFromMinorUnits(12345, 2) is "123.45".
func MustParseMoney ¶
MustParseMoney is ParseMoney for constants and tests. It panics on a bad value, so never give it anything that came off the wire.
func ParseMoney ¶
ParseMoney reads a decimal string from the wire.
func (Money) ClampToZero ¶
ClampToZero returns m, or zero when m is negative.
func (Money) FormatBonus ¶
FormatBonus renders bonus_amount, which the contract caps at 12 fractional digits while platform money allows 16. Clamping here keeps a 16-decimal currency from emitting a value the contract would reject.
func (Money) IsNegative ¶
IsNegative reports whether the amount is below zero.
func (Money) IsPositive ¶
IsPositive reports whether the amount is above zero.
func (Money) MarshalJSON ¶
MarshalJSON emits the wire form, so an amount can never be serialised as a JSON number.
func (Money) MinorUnits ¶
MinorUnits returns the unscaled integer value at Scale.
func (Money) Sub ¶
Sub returns m - other. The result may be negative, which is how an insufficient balance is detected.
func (*Money) UnmarshalJSON ¶
UnmarshalJSON accepts only the contract's decimal-string shape.
type RequestContext ¶
type RequestContext struct {
Route Route
// RawBody holds the exact bytes the signature was verified against.
RawBody []byte
// Headers has lower-cased keys.
Headers map[string]string
}
RequestContext carries everything about the request that is not part of the parsed body.
type RollbackRequest ¶
type RollbackRequest struct {
AccountID string
Currency string
// GameID is required by the contract and always sent by the platform, but
// the SDK accepts its absence and gives you "" rather than rejecting a
// request it could have served. Liberal in, strict out.
GameID string
// RoundIDProvider — note the name: rollback uses round_id_provider while
// betwin and finish use round_id.
RoundIDProvider string
Finished bool
Transactions []RollbackTransaction
SessionID string
}
RollbackRequest reverses transactions in a round.
type RollbackResult ¶
type RollbackResult struct {
Balance Money
RoundID string
Transactions []RollbackTransactionResult
}
RollbackResult answers a RollbackRequest.
type RollbackTransaction ¶
type RollbackTransaction struct {
IDProvider string
// OriginalIDProvider is the id_provider of the transaction being reversed.
OriginalIDProvider string
}
RollbackTransaction reverses one earlier transaction.
type RollbackTransactionResult ¶
type RollbackTransactionResult struct {
IDProvider string
// ID is your transaction id, or "" when there was nothing to reverse.
ID string
}
RollbackTransactionResult is your answer for one rollback transaction.
type Route ¶
type Route string
Route is one of the callback paths, exactly as the contract names them.
const ( RouteBalance Route = "/balance" RouteBetWin Route = "/betwin" RouteRollback Route = "/rollback" RouteFinish Route = "/finish" RouteFreespinsFinish Route = "/freespins/finish" )
The callback routes: the four every wallet serves, and RouteFreespinsFinish, which only a wallet that issues freespins does (see FreespinsFinishHandler).
func RouteFromPath ¶
RouteFromPath maps a request path to one of the callback routes, matching on the suffix so any mount prefix works. The longest route wins, so /wallet/freespins/finish is the freespins result and never the round's /finish.
type TransactionType ¶
type TransactionType string
TransactionType is "bet" or "win".
const ( TransactionBet TransactionType = "bet" TransactionWin TransactionType = "win" )
The two transaction types /betwin carries.
type WalletError ¶
type WalletError struct {
Status int
TwirpCode string
APICode string
Message string
// Balance is required for api_code 100, 105 and 106 and must be nil
// otherwise.
Balance *Money
}
WalletError is the error your handler returns to answer the platform with a specific api_code. Anything else you return becomes an opaque 500 — your message is never echoed to the platform.
func ErrAlreadyRolledBack ¶
func ErrAlreadyRolledBack() *WalletError
ErrAlreadyRolledBack builds api_code 409 — the transaction's id_provider was already rolled back (tombstoned).
func ErrBadRequest ¶
func ErrBadRequest(message string) *WalletError
ErrBadRequest builds api_code 400.
func ErrBetLimitReached ¶
func ErrBetLimitReached(balance Money) *WalletError
ErrBetLimitReached builds api_code 105.
func ErrCurrencyNotAllowed ¶
func ErrCurrencyNotAllowed(message string) *WalletError
ErrCurrencyNotAllowed builds api_code 154.
func ErrFreespinsStateTransition ¶ added in v1.2.0
func ErrFreespinsStateTransition(message string) *WalletError
ErrFreespinsStateTransition builds api_code 605 — the issue cannot take this result in its current state: on /freespins/finish, an issue you canceled.
func ErrGameForbidden ¶
func ErrGameForbidden(message string) *WalletError
ErrGameForbidden builds api_code 107.
func ErrInsufficientFunds ¶
func ErrInsufficientFunds(balance Money) *WalletError
ErrInsufficientFunds builds api_code 100.
func ErrInvalidFreespinsIssue ¶ added in v1.2.0
func ErrInvalidFreespinsIssue(message string) *WalletError
ErrInvalidFreespinsIssue builds api_code 610 — /freespins/finish names an issue_id you never issued.
func ErrInvalidPlayer ¶
func ErrInvalidPlayer(message string) *WalletError
ErrInvalidPlayer builds api_code 101.
func ErrInvalidSignature ¶
func ErrInvalidSignature() *WalletError
ErrInvalidSignature builds api_code 403 — with HTTP 400, as the contract requires.
func ErrMaxBetExceeded ¶
func ErrMaxBetExceeded(balance Money) *WalletError
ErrMaxBetExceeded builds api_code 106.
func ErrPlayerDisabled ¶
func ErrPlayerDisabled(message string) *WalletError
ErrPlayerDisabled builds api_code 110.
func ErrServiceUnavailable ¶
func ErrServiceUnavailable(message string) *WalletError
ErrServiceUnavailable builds api_code 503.
func WalletErrorWithAPICode ¶
func WalletErrorWithAPICode(apiCode, message string, balance *Money) (*WalletError, error)
WalletErrorWithAPICode is the escape hatch for an api_code without a dedicated constructor. It enforces the same balance rule, so it cannot be used to bypass it.
func (*WalletError) Envelope ¶
func (e *WalletError) Envelope() ErrorEnvelope
Envelope renders the wire form. It panics if a funds-related code lost its balance on the way, which can only happen through WalletErrorWithAPICode.
type WalletHandler ¶
type WalletHandler interface {
Balance(ctx context.Context, req BalanceRequest, rc RequestContext) (BalanceResult, error)
BetWin(ctx context.Context, req BetWinRequest, rc RequestContext) (BetWinResult, error)
Rollback(ctx context.Context, req RollbackRequest, rc RequestContext) (RollbackResult, error)
Finish(ctx context.Context, req FinishRequest, rc RequestContext) (FinishResult, error)
}
WalletHandler is the four methods you implement against your own ledger. Everything else — signature verification, parsing, validation, the Twirp error envelope — is the SDK's job.
Return a *WalletError to answer with a specific api_code. Any other error becomes an opaque 500 and its message never leaves your process.
A wallet that issues freespins implements FreespinsFinishHandler as well.
type WalletServer ¶
type WalletServer struct {
// contains filtered or unexported fields
}
WalletServer turns the callbacks into one pure function.
Dispatch takes bytes and a signature and returns a status and a body. No framework type crosses that boundary and it never sees a parsed body from the outside, so signing the wrong thing is not expressible.
func NewWalletServer ¶
func NewWalletServer(handler WalletHandler, apiSecret string, opts ...WalletServerOption) (*WalletServer, error)
NewWalletServer builds a server. It fails on an empty secret: the gateway treats an empty secret as an automatic verification failure, so a server built with one would authenticate nothing while looking healthy.
When handler also implements FreespinsFinishHandler, /freespins/finish is served by it.
func (*WalletServer) Dispatch ¶
func (s *WalletServer) Dispatch(ctx context.Context, route Route, rawBody []byte, signature string, headers map[string]string) DispatchResponse
Dispatch verifies, parses, validates and routes one callback.
func (*WalletServer) NewHandler ¶
func (s *WalletServer) NewHandler() http.Handler
NewHandler wires the callbacks into an http.Handler.
It matches on the path SUFFIX because the mount path belongs to you: the gateway POSTs to the full callback URL configured in the backoffice, prefix and all, so /api/v2/psp/betwin is as valid as /betwin.
type WalletServerOption ¶
type WalletServerOption func(*WalletServer)
WalletServerOption configures a WalletServer.
func WithMaxBodyBytes ¶
func WithMaxBodyBytes(n int) WalletServerOption
WithMaxBodyBytes overrides the request body cap.
func WithWarning ¶
func WithWarning(fn func(message string)) WalletServerOption
WithWarning sets the sink for diagnostics explaining WHY a signature failed. The default writes nothing; pass a logger to see them.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
examples
|
|
|
inmemory
Package inmemory is a complete, correct Beexar wallet held in memory.
|
Package inmemory is a complete, correct Beexar wallet held in memory. |
|
server
command
A complete Beexar wallet integration in one file.
|
A complete Beexar wallet integration in one file. |