Shift member communication to app channel and remove notify-email flow

This commit is contained in:
Joshua 2026-02-17 12:11:16 -08:00
parent 65744ef700
commit d58c92ad55
25 changed files with 188 additions and 197 deletions

View File

@ -64,6 +64,7 @@ docs/
marketplace.examples.md
handoff/
membership-backend-checklist.md
marketplace-backend-checklist.md
schemas/
offer.v1.schema.json
entitlement.v1.schema.json

View File

@ -150,38 +150,7 @@ Error (`400` tx mismatch):
}
```
## 5) `POST /secret/notify`
Request:
```json
{
"email": "user@example.com",
"designation_code": "0217073045482",
"designation_token": "0217-0730-4548-2",
"wallet": "0x3ea6cbf98d23e2cf7b6f4f9bb1fb4f50b710f2d5",
"locale": "en"
}
```
Success (`200`):
```json
{
"status": "saved"
}
```
Error (`422` invalid email):
```json
{
"error": "invalid_email",
"message": "Email format is invalid."
}
```
## 6) `GET /secret/membership/status`
## 5) `GET /secret/membership/status`
Request by wallet:

View File

@ -92,27 +92,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/MembershipStatusResponse'
/secret/notify:
post:
summary: Save optional notification email
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/NotifyRequest'
responses:
'200':
description: Notification saved
content:
application/json:
schema:
type: object
required: [status]
properties:
status:
type: string
enum: [saved]
components:
schemas:
WalletIntentRequest:
@ -265,19 +244,3 @@ components:
type: string
designation_code:
type: string
NotifyRequest:
type: object
required: [email, designation_code, wallet, locale]
properties:
email:
type: string
format: email
designation_code:
type: string
designation_token:
type: string
wallet:
type: string
pattern: '^0x[a-fA-F0-9]{40}$'
locale:
type: string

View File

@ -15,7 +15,6 @@ This matrix defines deterministic fail-closed behavior and user-facing outcomes.
| Confirm | Amount mismatch | Quote/tx comparator | Reject confirm | "Transaction does not match quote." |
| Confirm | Recipient mismatch | Quote/tx comparator | Reject confirm | "Destination contract mismatch." |
| Confirm | Node unavailable | RPC health | Fail closed | "Unable to confirm transaction. Purchase stays blocked." |
| Notify | Invalid email | Input validation | Reject notify | "Invalid email format." |
| Checkout | No membership | Gate check | Block purchase | "Membership required." |
| Checkout | Membership suspended/revoked | Gate check | Block purchase | "Membership inactive. Contact support." |
| Activation | Entitlement not active | Gate check | Block runtime | "License inactive. Activation blocked." |

View File

@ -0,0 +1,65 @@
# Backend Handoff Checklist: Marketplace Checkout and Entitlements
This checklist maps store behavior to required marketplace backend implementation.
## Required Endpoints
1. `GET /marketplace/offers`
2. `GET /marketplace/offers/{offer_id}`
3. `POST /marketplace/checkout/quote`
4. `POST /marketplace/checkout/confirm`
5. `GET /marketplace/entitlements?wallet=...`
## Required Gate Behavior
1. Quote endpoint must deny checkout when membership is not active.
2. Confirm endpoint must verify quote policy hash and tx match.
3. Entitlement state must default fail-closed for unknown values.
## Store Dependency Mapping
1. Store catalog requests `/marketplace/offers` (fallback to local JSON until live).
2. Store quote action posts selected `offer_id` and wallet.
3. Store expects quote payload with tx execution fields.
4. Store confirm path expects `entitlement_active` response.
## Quote Response Requirements
1. `quote_id`
2. `wallet`
3. `offer_id`
4. `currency`
5. `amount` or `amount_atomic + decimals`
6. `policy_hash`
7. `expires_at`
8. `tx` execution object or equivalent fields
## Confirm Response Requirements
1. `status = entitlement_active`
2. `entitlement_id`
3. `offer_id`
4. `wallet`
5. `tx_hash`
6. `policy_hash`
7. `activated_at`
## Persistence Requirements
1. Quote record with policy hash and expiry.
2. Confirm record linked to tx hash and entitlement id.
3. Entitlement lifecycle state with immutable issued event evidence.
## Security Requirements
1. Membership gate check on quote and confirm paths.
2. Quote TTL enforcement.
3. Tx chain, amount, and destination validation.
4. Idempotent confirm handling for repeated tx hash submissions.
## Done Criteria
1. Store can request quotes for active members only.
2. Confirm endpoint issues active entitlements deterministically.
3. Entitlement listing endpoint returns current state records.
4. API matches `docs/api/marketplace.openapi.yaml`.

View File

@ -8,7 +8,7 @@ This checklist maps current web behavior to required backend implementation.
2. `POST /secret/wallet/verify`
3. `POST /secret/membership/quote`
4. `POST /secret/membership/confirm`
5. `POST /secret/notify`
5. `GET /secret/membership/status`
## Web Behavior Dependency
@ -21,7 +21,7 @@ The landing page currently executes these actions in order:
5. Request membership quote.
6. Send wallet transaction (`eth_sendTransaction`) using returned tx params.
7. Confirm membership by tx hash.
8. Show acknowledged state and optional notify form.
8. Show acknowledged state and app download links.
If any endpoint is missing, flow fails closed and shows status error.
@ -69,15 +69,12 @@ Must return:
3. `display_token`
4. `tx_hash`
## Notify
## Membership Status
Must accept:
Must return:
1. `email`
2. `designation_code`
3. `designation_token`
4. `wallet`
5. `locale`
1. `status` (`active|none|suspended|revoked|unknown`)
2. selector echo (`wallet` and/or `designation_code`)
## Security Requirements
@ -97,7 +94,7 @@ Persist at minimum:
3. intent fields and verification time
4. quote fields and expiry
5. membership tx hash and activation timestamp
6. notification email link metadata
6. membership status resolution fields for wallet/designation lookups
## Observability Requirements
@ -108,7 +105,7 @@ Persist at minimum:
- verify success/fail
- quote success/fail
- confirm success/fail
- notify success/fail
- membership status lookups success/fail
## Done Criteria

View File

@ -33,7 +33,7 @@
5. Updated secret system architecture to wallet-first membership flow.
- Replaced SMS/email-first assumptions with wallet signature + membership mint sequence.
- Added membership-gate framing (`membership required`, `membership != product license`).
- Kept notification email optional and post-membership.
- Set post-membership value delivery to platform download and app-native communication.
6. Privacy policy hardening.
- Removed over-broad ad-tech assumptions.

View File

@ -73,7 +73,7 @@ This roadmap is intentionally step-based and dependency-ordered. No timeline com
## Step 11: Harden Compliance and Policy Surfaces
1. Terms clearly separate membership rights from license rights.
2. Privacy describes wallet/signature processing and optional notifications.
2. Privacy describes wallet/signature processing and app-native member communication.
3. Public copy avoids investment framing and speculative claims.
## Step 12: Operational Hardening
@ -89,4 +89,3 @@ This roadmap is intentionally step-based and dependency-ordered. No timeline com
- visit -> signature -> membership mint -> first entitlement purchase.
2. Tune copy, wallet guidance, and pricing policy using deterministic metrics.
3. Expand issuer ecosystem only after quality and support controls are stable.

View File

@ -26,7 +26,7 @@ Status key:
Implemented now:
1. Wallet-first landing flow with intent + signature + membership tx hooks.
2. Optional notification link step.
2. Post-mint app delivery step (`download your platform`) for immediate member value.
3. Membership-gated architecture spec.
4. Step-based roadmap without timelines.
5. Frozen v1 schemas and examples.

View File

@ -10,7 +10,7 @@ The Edut onboarding flow is deterministic and wallet-first:
4. Server verifies signature and creates/updates designation record.
5. Visitor mints paid EDUT membership on-chain (intent proof).
6. Server confirms transaction and marks membership active.
7. Visitor may optionally add notification email for launch updates.
7. Visitor downloads the EDUT platform app from post-mint success links.
This flow is the pre-launch identity and commerce envelope. It is not a throwaway waitlist.
@ -29,7 +29,8 @@ This flow is the pre-launch identity and commerce envelope. It is not a throwawa
11. Wallet submits membership mint transaction on Base.
12. Page confirms via `POST /secret/membership/confirm` and/or status poll.
13. UI shows `acknowledged · {token}` when membership is active.
14. Optional: user submits email via `POST /secret/notify`.
14. Post-mint success state presents `download your platform` links (Desktop/iOS/Android).
15. Member opens the app, signs in with the same wallet, and receives platform updates through app notifications.
Privacy and Terms links bypass flow and navigate normally.
@ -43,7 +44,7 @@ Landing page -> POST /secret/membership/quote
Wallet sends paid mint tx on Base
Landing page -> POST /secret/membership/confirm
Membership active -> acknowledged state
Optional notification email -> POST /secret/notify
Post-mint success -> app download links (Desktop/iOS/Android)
```
## Core Commerce Rule
@ -51,6 +52,7 @@ Optional notification email -> POST /secret/notify
1. Membership is required to purchase marketplace offers.
2. Membership is not a product/module license.
3. Offer-specific licenses/entitlements are purchased separately.
4. Membership purchase delivers initial platform access (download entry point) immediately after activation.
## Infrastructure
@ -61,18 +63,13 @@ Optional notification email -> POST /secret/notify
| API | `api.edut.ai/secret/wallet/verify` | Verify signature and bind wallet identity |
| API | `api.edut.ai/secret/membership/quote` | Return current payable membership quote |
| API | `api.edut.ai/secret/membership/confirm` | Confirm membership tx and activation state |
| API | `api.edut.ai/secret/notify` | Store optional launch notification email |
| Chain | Base | Membership mint settlement and evidence |
| Database | `/var/lib/edut/secrets.db` | Durable designation + membership + notification state |
| Database | `/var/lib/edut/secrets.db` | Durable designation + membership state |
## Deterministic State Machine
`pending_signature` -> `signature_verified` -> `pending_membership_mint` -> `membership_active`
Optional post-activation state:
- `notification_linked` (email saved)
Additional side states:
- `intent_expired`
@ -237,29 +234,6 @@ Response:
}
```
### 5) Optional Notification Link
#### `POST /secret/notify`
Request JSON:
```json
{
"email": "user@example.com",
"designation_code": "0217073045482",
"designation_token": "0217-0730-4548-2",
"wallet": "0xabc123...",
"locale": "en"
}
```
Behavior:
1. Validate email format and normalize case.
2. Bind email to active designation/membership record.
3. Keep idempotent updates by designation or wallet.
4. Record timestamp and source IP for abuse controls.
## Security Controls
1. Intent TTL and one-time nonce consumption.
@ -295,8 +269,6 @@ CREATE TABLE IF NOT EXISTS designations (
membership_quote_expires_at DATETIME,
membership_tx_hash TEXT,
membership_activated_at DATETIME,
notification_email TEXT,
notification_linked_at DATETIME,
origin TEXT,
locale TEXT,
created_at DATETIME DEFAULT (datetime('now'))
@ -333,9 +305,6 @@ location /secret/membership/confirm {
proxy_pass http://127.0.0.1:9091;
}
location /secret/notify {
proxy_pass http://127.0.0.1:9091;
}
```
## Summary

View File

@ -20,7 +20,8 @@ The site should feel precise and established while avoiding disclosure of privat
6. They mint EDUT membership as a paid on-chain commitment.
7. Membership confirmation binds durable account state to the wallet.
8. Final state shows `acknowledged · {token}`.
9. Optional `notify me` captures email for launch updates.
9. Post-mint success reveals `download your platform` links immediately.
10. Member communication occurs through the app after wallet sign-in (native notification channel).
The flow should feel controlled and ambient, not like a conventional signup form.
@ -49,7 +50,7 @@ The flow should feel controlled and ambient, not like a conventional signup form
- `edut.ai`: primary public surface and business identity.
- `edut.dev`: developer-facing domain (same landing for now).
- `api.edut.ai`: API endpoints for wallet intent/verify, membership quote/confirm, and optional notification linking.
- `api.edut.ai`: API endpoints for wallet intent/verify and membership quote/confirm.
- `/privacy` and `/terms`: legal pages (English authoritative).
## Messaging Boundaries
@ -89,7 +90,7 @@ The designation and membership flow is the pre-launch identity envelope:
- Factor 1: cryptographic wallet possession (signature proof)
- Factor 2: economic commitment (paid membership mint)
- Optional channel: notification email binding
- Delivery channel: app install + wallet sign-in
- Shared binding: one server-generated designation record (`code` + `auth_token`)
When launch activation opens, this record becomes the continuity bridge for deployment onboarding and activation messaging.

View File

@ -263,26 +263,12 @@
opacity: 1;
}
.notify-form {
.download-links {
display: flex;
align-items: center;
gap: 10px;
}
.notify-input {
border: 0;
border-bottom: 1px solid #606264;
background: transparent;
color: #3a3d42;
font-family: 'IBM Plex Mono', 'Courier New', Courier, monospace;
font-size: 12px;
letter-spacing: 0.08em;
padding: 5px 3px;
min-width: 220px;
}
.notify-input:focus {
outline: none;
border-bottom-color: #2c2c2c;
justify-content: center;
gap: 14px;
flex-wrap: wrap;
}
/* Footer */
@ -416,7 +402,7 @@
<div class="flow-ui" id="flow-ui">
<button id="continue-action" class="ghost-action localizable flow-hidden" data-i18n="continue_label" type="button">continue</button>
<div id="wallet-panel" class="flow-panel flow-hidden">
<p class="flow-line localizable" data-i18n="wallet_intro">receive your designation token</p>
<p class="flow-line localizable" data-i18n="wallet_intro">mint your membership</p>
<p class="flow-line subtle localizable" data-i18n="wallet_fact_no_tx">No transaction. Signature only.</p>
<p class="flow-line subtle localizable" data-i18n="wallet_fact_seed">Never share your seed phrase.</p>
<div class="flow-actions">
@ -429,12 +415,14 @@
</div>
</div>
<div id="flow-status" class="flow-status flow-hidden" aria-live="polite"></div>
<div id="notify-panel" class="notify-panel flow-hidden">
<button id="notify-open" class="flow-link localizable" data-i18n="notify_me" type="button">notify me</button>
<form id="notify-form" class="notify-form flow-hidden" autocomplete="on">
<input id="notify-email" class="notify-input localizable" data-i18n-placeholder="notify_placeholder" type="email" placeholder="email" required />
<button id="notify-submit" class="flow-link localizable" data-i18n="notify_submit" type="submit">save</button>
</form>
<div id="delivery-panel" class="notify-panel flow-hidden">
<p class="flow-line localizable" data-i18n="download_heading">download your platform</p>
<div class="download-links">
<a id="download-desktop" class="flow-link localizable" data-i18n="download_desktop" href="/downloads/desktop">desktop</a>
<a id="download-ios" class="flow-link localizable" data-i18n="download_ios" href="/downloads/ios">iOS</a>
<a id="download-android" class="flow-link localizable" data-i18n="download_android" href="/downloads/android">android</a>
</div>
<p class="flow-line subtle localizable" data-i18n="app_notifications_note">member updates are delivered inside the app after wallet sign-in.</p>
</div>
</div>
<span class="sr-only localizable" id="interaction-hint" data-i18n="interaction_hint">Click anywhere on the page to begin your access request.</span>
@ -490,10 +478,7 @@ const walletHave = document.getElementById('wallet-have');
const walletNeed = document.getElementById('wallet-need');
const walletInstallHelp = document.getElementById('wallet-install-help');
const flowStatus = document.getElementById('flow-status');
const notifyPanel = document.getElementById('notify-panel');
const notifyOpen = document.getElementById('notify-open');
const notifyForm = document.getElementById('notify-form');
const notifyEmail = document.getElementById('notify-email');
const deliveryPanel = document.getElementById('delivery-panel');
const privacyLink = document.getElementById('privacyLink');
const termsLink = document.getElementById('termsLink');
@ -564,6 +549,12 @@ function renderAcknowledged(state) {
el.classList.add('visible');
}
function showPostMintPanel() {
hideElement(continueAction);
hideElement(walletPanel);
showElement(deliveryPanel);
}
function setFlowStatus(key, fallback, isError) {
flowStatus.textContent = t(key, fallback);
flowStatus.classList.toggle('error', !!isError);
@ -683,7 +674,10 @@ function applyLocale(bundle, lang) {
});
const saved = getStoredAcknowledgement();
if (saved) renderAcknowledged(saved);
if (saved) {
renderAcknowledged(saved);
showPostMintPanel();
}
}
async function initializeLocale() {
@ -833,9 +827,7 @@ async function startWalletFlow() {
};
saveAcknowledgement(ackState);
renderAcknowledged(ackState);
hideElement(continueAction);
hideElement(walletPanel);
showElement(notifyPanel);
showPostMintPanel();
setFlowStatus('membership_active', 'Membership active. Designation acknowledged.', false);
} catch (err) {
const message = err && err.message ? err.message : 'Wallet flow failed.';
@ -843,26 +835,6 @@ async function startWalletFlow() {
}
}
async function submitNotifyEmail(event) {
event.preventDefault();
const email = notifyEmail.value.trim();
if (!email) return;
const ack = getStoredAcknowledgement();
try {
await postJSON('/secret/notify', {
email,
designation_code: ack && ack.code ? ack.code : null,
designation_token: ack && ack.token ? ack.token : null,
wallet: ack && ack.wallet ? ack.wallet : null,
locale: localStorage.getItem('edut_lang') || 'en',
});
setFlowStatus('notify_saved', 'Notification saved.', false);
notifyForm.classList.add('flow-hidden');
} catch (err) {
setFlowStatus('notify_failed', 'Could not save notification preference.', true);
}
}
function onFirstInteractionReveal() {
if (flowState.firstInteraction || getStoredAcknowledgement()) return;
flowState.firstInteraction = true;
@ -880,10 +852,6 @@ continueAction.addEventListener('click', () => {
});
walletHave.addEventListener('click', startWalletFlow);
walletNeed.addEventListener('click', openWalletInstallHints);
notifyOpen.addEventListener('click', () => {
notifyForm.classList.toggle('flow-hidden');
});
notifyForm.addEventListener('submit', submitNotifyEmail);
// Keyboard support: Enter/Space triggers same as click
document.body.addEventListener('keydown', (e) => {

View File

@ -157,7 +157,7 @@
<h2>Information We Collect</h2>
<p><strong>Information you provide directly.</strong> When you interact with our sites — including connecting a wallet, signing a designation message, entering optional notification details, contacting support, or completing a transaction — we may collect your wallet address, signed message payloads, email address, contact details, billing details, message content, and any other information you choose to provide.</p>
<p><strong>Information you provide directly.</strong> When you interact with our sites — including connecting a wallet, signing a designation message, downloading platform software, contacting support, or completing a transaction — we may collect your wallet address, signed message payloads, contact details, billing details, message content, and any other information you choose to provide.</p>
<p><strong>Information collected automatically.</strong> When you visit our sites, we may collect technical and usage information such as IP address, browser type, operating system, referring URLs, pages viewed, timestamps, locale preferences, and interaction events. This information is collected through server logs, local storage, cookies, and similar technologies.</p>
@ -169,7 +169,7 @@
<p>We may use information we collect to operate, maintain, and improve our websites, products, and services; process transactions and send related confirmations, invoices, and receipts; communicate with you regarding security, operations, product updates, and support; analyze reliability and usage trends; detect and prevent fraud, abuse, and unauthorized access; comply with legal obligations; and enforce our agreements.</p>
<p><strong>Verified-channel purpose.</strong> Where designation workflows are used, we process cryptographic signature data to confirm wallet control, prevent automated abuse, bind protocol records to a stable identity anchor, and maintain auditable activation continuity. If you provide an optional notification email, we use it to deliver launch and operational notices tied to your designation.</p>
<p><strong>Verified-channel purpose.</strong> Where designation workflows are used, we process cryptographic signature data to confirm wallet control, prevent automated abuse, bind protocol records to a stable identity anchor, and maintain auditable activation continuity. Member operational notices are delivered through platform software after wallet sign-in.</p>
<h2>Blockchain and Wallet Data</h2>
<p>Wallet addresses and related signature metadata may be processed to verify cryptographic intent and establish designation records. Public blockchain networks are independently operated systems; if designation or licensing records are written on-chain, related transaction data may be publicly visible and immutable by design. We do not control third-party blockchain explorers or wallet software.</p>
@ -178,7 +178,7 @@
<p><strong>Service providers.</strong> We may share your information with third-party providers that support hosting, infrastructure, email delivery, wallet connectivity infrastructure, support tooling, analytics, and payment processing. These providers are contractually obligated to process information only for authorized service purposes.</p>
<p><strong>Communications providers.</strong> We may use communications processors for operational email delivery and response handling. These providers process contact information and message metadata as necessary to transmit and verify communications.</p>
<p><strong>Communications providers.</strong> We may use communications processors for operational email support handling and app-notification infrastructure. These providers process message metadata as necessary to transmit and verify communications.</p>
<p><strong>Legal requirements.</strong> We may disclose your information if required to do so by law, regulation, legal process, or governmental request, or when we believe disclosure is necessary to protect our rights, your safety, or the safety of others, or to investigate fraud or respond to a government request.</p>

View File

@ -51,5 +51,10 @@
"notify_placeholder": "البريد الإلكتروني",
"notify_submit": "حفظ",
"notify_saved": "تم حفظ الإشعار.",
"notify_failed": "تعذر حفظ تفضيل الإشعار."
"notify_failed": "تعذر حفظ تفضيل الإشعار.",
"download_heading": "نزّل منصتك",
"download_desktop": "سطح المكتب",
"download_ios": "iOS",
"download_android": "أندرويد",
"app_notifications_note": "تصل تحديثات الأعضاء داخل التطبيق بعد تسجيل الدخول بالمحفظة."
}

View File

@ -51,5 +51,10 @@
"notify_placeholder": "E-Mail",
"notify_submit": "speichern",
"notify_saved": "Benachrichtigung gespeichert.",
"notify_failed": "Benachrichtigungseinstellung konnte nicht gespeichert werden."
"notify_failed": "Benachrichtigungseinstellung konnte nicht gespeichert werden.",
"download_heading": "lade deine plattform herunter",
"download_desktop": "desktop",
"download_ios": "iOS",
"download_android": "android",
"app_notifications_note": "mitglieder-updates werden in der app nach wallet-anmeldung zugestellt."
}

View File

@ -51,5 +51,10 @@
"notify_placeholder": "email",
"notify_submit": "save",
"notify_saved": "Notification saved.",
"notify_failed": "Could not save notification preference."
"notify_failed": "Could not save notification preference.",
"download_heading": "download your platform",
"download_desktop": "desktop",
"download_ios": "iOS",
"download_android": "android",
"app_notifications_note": "member updates are delivered inside the app after wallet sign-in."
}

View File

@ -51,5 +51,10 @@
"notify_placeholder": "correo",
"notify_submit": "guardar",
"notify_saved": "Notificación guardada.",
"notify_failed": "No se pudo guardar la preferencia de notificación."
"notify_failed": "No se pudo guardar la preferencia de notificación.",
"download_heading": "descarga tu plataforma",
"download_desktop": "escritorio",
"download_ios": "iOS",
"download_android": "android",
"app_notifications_note": "las actualizaciones para miembros se entregan dentro de la app tras iniciar sesion con wallet."
}

View File

@ -51,5 +51,10 @@
"notify_placeholder": "e-mail",
"notify_submit": "enregistrer",
"notify_saved": "Notification enregistree.",
"notify_failed": "Impossible denregistrer la preference de notification."
"notify_failed": "Impossible denregistrer la preference de notification.",
"download_heading": "telechargez votre plateforme",
"download_desktop": "bureau",
"download_ios": "iOS",
"download_android": "android",
"app_notifications_note": "les mises a jour membres sont envoyees dans lapp apres connexion du wallet."
}

View File

@ -51,5 +51,10 @@
"notify_placeholder": "אימייל",
"notify_submit": "שמור",
"notify_saved": "ההתראה נשמרה.",
"notify_failed": "לא ניתן לשמור את העדפת ההתראה."
"notify_failed": "לא ניתן לשמור את העדפת ההתראה.",
"download_heading": "הורד את הפלטפורמה שלך",
"download_desktop": "דסקטופ",
"download_ios": "iOS",
"download_android": "אנדרואיד",
"app_notifications_note": "עדכוני חברים נמסרים בתוך האפליקציה לאחר התחברות בארנק."
}

View File

@ -51,5 +51,10 @@
"notify_placeholder": "ईमेल",
"notify_submit": "सहेजें",
"notify_saved": "सूचना सहेज ली गई।",
"notify_failed": "सूचना वरीयता सहेजी नहीं जा सकी।"
"notify_failed": "सूचना वरीयता सहेजी नहीं जा सकी।",
"download_heading": "अपना प्लेटफ़ॉर्म डाउनलोड करें",
"download_desktop": "डेस्कटॉप",
"download_ios": "iOS",
"download_android": "एंड्रॉइड",
"app_notifications_note": "सदस्य अपडेट वॉलेट साइन-इन के बाद ऐप के अंदर दिए जाते हैं।"
}

View File

@ -51,5 +51,10 @@
"notify_placeholder": "メール",
"notify_submit": "保存",
"notify_saved": "通知設定を保存しました。",
"notify_failed": "通知設定を保存できませんでした。"
"notify_failed": "通知設定を保存できませんでした。",
"download_heading": "プラットフォームをダウンロード",
"download_desktop": "デスクトップ",
"download_ios": "iOS",
"download_android": "android",
"app_notifications_note": "メンバー向け更新はウォレットサインイン後にアプリ内で配信されます。"
}

View File

@ -51,5 +51,10 @@
"notify_placeholder": "이메일",
"notify_submit": "저장",
"notify_saved": "알림 설정이 저장되었습니다.",
"notify_failed": "알림 설정을 저장하지 못했습니다."
"notify_failed": "알림 설정을 저장하지 못했습니다.",
"download_heading": "플랫폼 다운로드",
"download_desktop": "데스크톱",
"download_ios": "iOS",
"download_android": "android",
"app_notifications_note": "회원 업데이트는 지갑 로그인 후 앱 안에서 전달됩니다."
}

View File

@ -51,5 +51,10 @@
"notify_placeholder": "e-mail",
"notify_submit": "salvar",
"notify_saved": "Notificação salva.",
"notify_failed": "Não foi possível salvar a preferência de notificação."
"notify_failed": "Não foi possível salvar a preferência de notificação.",
"download_heading": "baixe sua plataforma",
"download_desktop": "desktop",
"download_ios": "iOS",
"download_android": "android",
"app_notifications_note": "as atualizacoes para membros sao entregues no app apos login da wallet."
}

View File

@ -51,5 +51,10 @@
"notify_placeholder": "email",
"notify_submit": "сохранить",
"notify_saved": "Уведомление сохранено.",
"notify_failed": "Не удалось сохранить настройки уведомлений."
"notify_failed": "Не удалось сохранить настройки уведомлений.",
"download_heading": "скачайте свою платформу",
"download_desktop": "десктоп",
"download_ios": "iOS",
"download_android": "android",
"app_notifications_note": "обновления для участников доставляются в приложении после входа через кошелек."
}

View File

@ -51,5 +51,10 @@
"notify_placeholder": "邮箱",
"notify_submit": "保存",
"notify_saved": "通知已保存。",
"notify_failed": "无法保存通知偏好。"
"notify_failed": "无法保存通知偏好。",
"download_heading": "下载你的平台",
"download_desktop": "桌面版",
"download_ios": "iOS",
"download_android": "安卓",
"app_notifications_note": "会员更新会在钱包登录后通过应用内发送。"
}