feat(payments): VK Votes payment rail #224

Merged
developer merged 5 commits from feature/payment-intake-vk into development 2026-07-09 18:23:24 +00:00
Owner

What

The VK Votes ("голоса") payment rail, end to end, reusing the intake engine from the direct
rail. The wallet.order endpoint branches by rail: a VK context opens a pending order (provider
vk) and returns its id; the client passes it to VKWebAppShowOrderBox.

Mechanism. VK's two-phase payment callback is verified at the gateway with the app protected
key (GATEWAY_VK_APP_SECRET, already deployed) and proxied to a backend intake handler:

  • get_item → returns the ordered pack's title and vote price;
  • a chargeable order_status_change → the same Fund (source vk, currency VOTE), idempotent
    on VK's own order id, and records a succeeded event, so the dispatcher push refreshes the
    wallet in place.

VK self-handles the receipt (D41), so nothing is sent.

Tests

Unit: the VK signature verifier (vkpay — MD5, sorted key=value, secret appended; valid / tampered
/ wrong-secret / missing). Integration (Postgres): a VK order → get_item lookup → credit the vk
segment once → a duplicate VK callback credits nothing. UI: check / test / build / wallet e2e
(the direct path is unchanged; the VK branch is exercised on-device).

Review on the contour

In the VK Mini App, a chip pack shows priced in votes; buying opens VK's payment box; paying
credits the chips (via the push). VK vote-packs are seeded on the contour (50 → 10, 100 → 20 votes).

Owner setup

The VK app payment callback = https://scrabble.iliadenisov.ru/pay/vk/callback is set; the
secret is GATEWAY_VK_APP_SECRET (already deployed). Nothing else to wire.

⚠ Pre-prod: VK has no separate prod callback URL — the app callback points at the contour host,
so swap it to the prod host before deploying payments to prod (noted in the plan/memory).

Remaining (this stack)

The TG-Stars rail and refunds; plus hiding the ad banner on a no-ads purchase (a spend-path signal,
deferred with your agreement).

## What The **VK Votes ("голоса") payment rail**, end to end, reusing the intake engine from the direct rail. The `wallet.order` endpoint branches by rail: a VK context opens a pending order (provider `vk`) and returns its id; the client passes it to `VKWebAppShowOrderBox`. **Mechanism.** VK's two-phase payment callback is **verified at the gateway** with the app protected key (`GATEWAY_VK_APP_SECRET`, already deployed) and proxied to a backend intake handler: - `get_item` → returns the ordered pack's title and vote price; - a chargeable `order_status_change` → the **same `Fund`** (source `vk`, currency `VOTE`), idempotent on **VK's own order id**, and records a `succeeded` event, so the dispatcher push refreshes the wallet in place. VK self-handles the receipt (D41), so nothing is sent. ## Tests Unit: the VK signature verifier (`vkpay` — MD5, sorted `key=value`, secret appended; valid / tampered / wrong-secret / missing). Integration (Postgres): a VK order → get_item lookup → credit the vk segment once → a duplicate VK callback credits nothing. UI: `check` / `test` / `build` / wallet e2e (the direct path is unchanged; the VK branch is exercised on-device). ## Review on the contour In the **VK Mini App**, a chip pack shows priced in **votes**; buying opens VK's payment box; paying credits the chips (via the push). VK vote-packs are seeded on the contour (50 → 10, 100 → 20 votes). ## Owner setup The VK app **payment callback = `https://scrabble.iliadenisov.ru/pay/vk/callback`** is set; the secret is `GATEWAY_VK_APP_SECRET` (already deployed). Nothing else to wire. **⚠ Pre-prod:** VK has no separate prod callback URL — the app callback points at the contour host, so **swap it to the prod host before deploying payments to prod** (noted in the plan/memory). ## Remaining (this stack) The TG-Stars rail and refunds; plus hiding the ad banner on a no-ads purchase (a spend-path signal, deferred with your agreement).
developer added 2 commits 2026-07-09 17:28:22 +00:00
Add the vkpay package: verify a VK Mini Apps payment ("голоса") callback
signature — MD5 (mandated by VK's payment protocol) of the sig-excluded
parameters, sorted by name and concatenated key=value, with the app secret
appended; case-insensitive over the hex digest. Unit-tested (valid / tampered /
wrong-secret / missing). First piece of the VK rail; the two-phase callback
handler, the VK order branch and the client bridge follow.
feat(payments): VK Votes payment rail
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Successful in 11s
CI / integration (pull_request) Successful in 18s
CI / ui (pull_request) Successful in 1m10s
CI / conformance (pull_request) Successful in 9s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m45s
3bb9f10fad
Wire the VK Mini Apps ("голоса") rail end to end, reusing the intake engine. The
wallet.order endpoint branches by rail: a VK context opens a pending order
(provider vk) and returns its id, which the client passes to VKWebAppShowOrderBox.
VK's two-phase payment callback is verified at the gateway with the app protected
key (GATEWAY_VK_APP_SECRET) and proxied to a backend intake handler: get_item
returns the ordered pack's title and vote price; a chargeable order_status_change
credits the vk segment exactly once (the same Fund, idempotent on VK's own order
id) and records a succeeded event, so the dispatcher push refreshes the wallet.
Integration test for the VK order->credit path.
developer added 1 commit 2026-07-09 17:57:10 +00:00
fix(ui): VK-iOS shows the pack CTA disabled with a toast, not a failed buy
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Successful in 10s
CI / integration (pull_request) Successful in 22s
CI / ui (pull_request) Successful in 1m9s
CI / conformance (pull_request) Successful in 10s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m55s
f48ebf2151
Money purchases are not permitted in the VK iOS app (Apple ToS), and the backend
already refuses them (the CreateOrder VK-iOS freeze). Show the pack "Buy" muted
there and explain on tap ("purchases are not available on this platform"),
instead of letting the tap hit a 403 and a generic error toast. The storefront
still lists the packs.
developer added 1 commit 2026-07-09 18:11:36 +00:00
fix(ui): correct the VK-iOS purchase block + add an iOS store note
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Successful in 10s
CI / integration (pull_request) Successful in 21s
CI / ui (pull_request) Successful in 1m9s
CI / conformance (pull_request) Successful in 10s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m59s
f6aa0ba4b0
The VK-iOS detection used platformSubtype(), which is server-derived for VK and
so never reported iOS on the client — the pack CTA stayed active and a tap hit a
403 with a generic error. Read the device family from the signed vk_platform
launch param (normalizeSubtype(vkPlatform())) instead. Under the store header on
VK-iOS, add a note that purchases are prohibited by Apple policy, linking to the
web version (opened through VK's external-link route).
developer added 1 commit 2026-07-09 18:18:43 +00:00
fix(ui): point the VK-iOS "other version" link at the site root
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Successful in 10s
CI / integration (pull_request) Successful in 19s
CI / ui (pull_request) Successful in 1m23s
CI / conformance (pull_request) Successful in 9s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m55s
e33b9864c8
The landing at / lists every version's entry; /app/ dropped the player straight
into the web game. Link the iOS store note to the root instead.
owner approved these changes 2026-07-09 18:18:57 +00:00
developer merged commit 890a887257 into development 2026-07-09 18:23:24 +00:00
developer deleted branch feature/payment-intake-vk 2026-07-09 18:23:24 +00:00
Sign in to join this conversation.
No Reviewers
No Label
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: developer/scrabble-game#224