feat(payments): order-flow and fund credit engine + the Robokassa adapter
Add the payment-intake write path (provider-agnostic) and the Robokassa direct-rail glue, both unit-tested; transport, wire and UI follow. - payments: extend the ledger insert to thread order_id/provider/ provider_payment_id (spend/grant pass nil); add the order store (create/read/expire + a pack-price loader) and the fund credit — a fund ledger row + a guarded balance upsert + mark-paid in one tx, idempotent on the (provider, provider_payment_id) unique index, cache invalidated after commit. A valid callback is honoured even on an expired order. Service CreateOrder/Fund/ExpireOrders; Money.Major for the provider amount field. - robokassa: build the signed hosted-payment URL (SHA-256, order id via Shp_order, InvId unused) and verify the Result callback signature (Password2), extracting the order and amount. Receipt/fiscalisation is configured shop-side, so no Receipt parameter is sent.
This commit is contained in:
@@ -147,12 +147,13 @@ func (m Money) Cmp(o Money) (int, error) {
|
||||
}
|
||||
}
|
||||
|
||||
// String renders the amount as "<value> <currency>", with the currency's
|
||||
// fractional digits and no floating point (e.g. "149.50 RUB", "250 XTR").
|
||||
func (m Money) String() string {
|
||||
// Major renders the amount as a decimal string without the currency, with the currency's
|
||||
// fractional digits and no floating point (e.g. "149.50", "250") — the form a provider's amount
|
||||
// field (Robokassa OutSum) takes.
|
||||
func (m Money) Major() string {
|
||||
scale := m.currency.minorPerUnit()
|
||||
if scale == 1 {
|
||||
return fmt.Sprintf("%d %s", m.minor, m.currency)
|
||||
return fmt.Sprintf("%d", m.minor)
|
||||
}
|
||||
neg := m.minor < 0
|
||||
abs := m.minor
|
||||
@@ -167,5 +168,10 @@ func (m Money) String() string {
|
||||
if neg {
|
||||
sign = "-"
|
||||
}
|
||||
return fmt.Sprintf("%s%d.%0*d %s", sign, abs/scale, width, abs%scale, m.currency)
|
||||
return fmt.Sprintf("%s%d.%0*d", sign, abs/scale, width, abs%scale)
|
||||
}
|
||||
|
||||
// String renders the amount as "<value> <currency>" (e.g. "149.50 RUB", "250 XTR").
|
||||
func (m Money) String() string {
|
||||
return m.Major() + " " + string(m.currency)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user