diff --git a/app/src/main/java/com/electricdreams/numo/feature/items/ItemSelectionActivity.kt b/app/src/main/java/com/electricdreams/numo/feature/items/ItemSelectionActivity.kt
index ac3909ea..f9eea9a3 100644
--- a/app/src/main/java/com/electricdreams/numo/feature/items/ItemSelectionActivity.kt
+++ b/app/src/main/java/com/electricdreams/numo/feature/items/ItemSelectionActivity.kt
@@ -1,3 +1,4 @@
+
package com.electricdreams.numo.feature.items
import android.animation.AnimatorSet
@@ -13,6 +14,7 @@ import android.widget.ImageButton
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.TextView
+import android.widget.Toast
import com.google.android.flexbox.FlexboxLayout
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AlertDialog
@@ -312,8 +314,10 @@ class ItemSelectionActivity : AppCompatActivity() {
val basketId = ensureBasketSaved()
if (basketId != null) {
checkoutHandler.savedBasketId = basketId
+ checkoutHandler.proceedToCheckout()
+ } else {
+ Toast.makeText(this, R.string.item_selection_toast_save_basket_first, Toast.LENGTH_SHORT).show()
}
- checkoutHandler.proceedToCheckout()
}
saveButton.setOnClickListener {
diff --git a/app/src/main/java/com/electricdreams/numo/feature/items/handlers/CheckoutHandler.kt b/app/src/main/java/com/electricdreams/numo/feature/items/handlers/CheckoutHandler.kt
index d8da7631..f0fff0d9 100644
--- a/app/src/main/java/com/electricdreams/numo/feature/items/handlers/CheckoutHandler.kt
+++ b/app/src/main/java/com/electricdreams/numo/feature/items/handlers/CheckoutHandler.kt
@@ -51,45 +51,17 @@ class CheckoutHandler(
// Determine how to format the amount for PaymentRequestActivity
val formattedAmount = formatPaymentAmount(fiatTotal, satsTotal)
- // Create a snapshot of the basket BEFORE clearing it
- // This preserves the checkout data for receipt generation
- val checkoutBasket = CheckoutBasket.fromBasketManager(
- basketManager = basketManager,
- currency = currencyManager.getCurrentCurrency(),
- bitcoinPrice = if (btcPrice > 0) btcPrice else null,
- totalSatoshis = totalSatoshis,
- )
- val checkoutBasketJson = checkoutBasket.toJson()
-
- android.util.Log.d("CheckoutHandler", "Captured basket with ${checkoutBasket.items.size} items, total: $totalSatoshis sats")
- android.util.Log.d("CheckoutHandler", "Basket JSON size: ${checkoutBasketJson.length} chars")
-
// Clear basket before navigating away so UI state is clean when we return
basketManager.clearBasket()
- // Decide whether to show tip selection first or go directly to payment request
val tipsManager = TipsManager.getInstance(activity)
- if (tipsManager.tipsEnabled) {
- // Route through beautiful tip selection screen first
- val intent = Intent(activity, TipSelectionActivity::class.java).apply {
- putExtra(TipSelectionActivity.EXTRA_PAYMENT_AMOUNT, totalSatoshis)
- putExtra(TipSelectionActivity.EXTRA_FORMATTED_AMOUNT, formattedAmount)
- putExtra(TipSelectionActivity.EXTRA_CHECKOUT_BASKET_JSON, checkoutBasketJson)
- // Preserve saved basket association all the way through to PaymentRequestActivity
- savedBasketId?.let { putExtra(PaymentRequestActivity.EXTRA_SAVED_BASKET_ID, it) }
- }
- activity.startActivity(intent)
- } else {
- // Go directly to payment request without tips
- val intent = Intent(activity, PaymentRequestActivity::class.java).apply {
- putExtra(PaymentRequestActivity.EXTRA_PAYMENT_AMOUNT, totalSatoshis)
- putExtra(PaymentRequestActivity.EXTRA_FORMATTED_AMOUNT, formattedAmount)
- putExtra(PaymentRequestActivity.EXTRA_CHECKOUT_BASKET_JSON, checkoutBasketJson)
- savedBasketId?.let { putExtra(PaymentRequestActivity.EXTRA_SAVED_BASKET_ID, it) }
- }
- activity.startActivity(intent)
+ val intent = Intent(activity, if (tipsManager.tipsEnabled) TipSelectionActivity::class.java else PaymentRequestActivity::class.java).apply {
+ putExtra(PaymentRequestActivity.EXTRA_PAYMENT_AMOUNT, totalSatoshis)
+ putExtra(PaymentRequestActivity.EXTRA_FORMATTED_AMOUNT, formattedAmount)
+ savedBasketId?.let { putExtra(PaymentRequestActivity.EXTRA_SAVED_BASKET_ID, it) }
}
+ activity.startActivity(intent)
activity.finish()
}
diff --git a/app/src/main/java/com/electricdreams/numo/feature/tips/TipSelectionActivity.kt b/app/src/main/java/com/electricdreams/numo/feature/tips/TipSelectionActivity.kt
index 72bf81f3..feb10cfc 100644
--- a/app/src/main/java/com/electricdreams/numo/feature/tips/TipSelectionActivity.kt
+++ b/app/src/main/java/com/electricdreams/numo/feature/tips/TipSelectionActivity.kt
@@ -42,7 +42,7 @@ class TipSelectionActivity : AppCompatActivity() {
private var formattedAmount: String = ""
private var entryCurrency: Currency = Currency.USD
private var enteredAmountFiat: Long = 0
- private var checkoutBasketJson: String? = null
+ private var basketId: String? = null
// State
private var selectedTipSats: Long = 0
@@ -123,7 +123,7 @@ class TipSelectionActivity : AppCompatActivity() {
private fun initializeFromIntent() {
paymentAmountSats = intent.getLongExtra(EXTRA_PAYMENT_AMOUNT, 0)
formattedAmount = intent.getStringExtra(EXTRA_FORMATTED_AMOUNT) ?: ""
- checkoutBasketJson = intent.getStringExtra(EXTRA_CHECKOUT_BASKET_JSON)
+ basketId = intent.getStringExtra(EXTRA_BASKET_ID)
// Parse entry currency
val parsedAmount = Amount.parse(formattedAmount)
@@ -852,9 +852,7 @@ class TipSelectionActivity : AppCompatActivity() {
putExtra(EXTRA_TIP_PERCENTAGE, selectedTipPercentage)
putExtra(EXTRA_BASE_AMOUNT_SATS, paymentAmountSats)
putExtra(EXTRA_BASE_FORMATTED_AMOUNT, formattedAmount)
- checkoutBasketJson?.let {
- putExtra(PaymentRequestActivity.EXTRA_CHECKOUT_BASKET_JSON, it)
- }
+ basketId?.let { putExtra(PaymentRequestActivity.EXTRA_SAVED_BASKET_ID, it) }
}
startActivityForResult(intent, REQUEST_CODE_PAYMENT)
@@ -902,7 +900,7 @@ class TipSelectionActivity : AppCompatActivity() {
companion object {
const val EXTRA_PAYMENT_AMOUNT = "payment_amount"
const val EXTRA_FORMATTED_AMOUNT = "formatted_amount"
- const val EXTRA_CHECKOUT_BASKET_JSON = "checkout_basket_json"
+ const val EXTRA_BASKET_ID = "basket_id"
const val EXTRA_TIP_AMOUNT_SATS = "tip_amount_sats"
const val EXTRA_TIP_PERCENTAGE = "tip_percentage"
const val EXTRA_BASE_AMOUNT_SATS = "base_amount_sats"
diff --git a/app/src/main/java/com/electricdreams/numo/payment/PaymentMethodHandler.kt b/app/src/main/java/com/electricdreams/numo/payment/PaymentMethodHandler.kt
index 899cb5a0..8ed9d9d0 100644
--- a/app/src/main/java/com/electricdreams/numo/payment/PaymentMethodHandler.kt
+++ b/app/src/main/java/com/electricdreams/numo/payment/PaymentMethodHandler.kt
@@ -19,11 +19,11 @@ class PaymentMethodHandler(
) {
/** Show payment method dialog for the specified amount */
- fun showPaymentMethodDialog(amount: Long, formattedAmount: String, checkoutBasketJson: String? = null) {
+ fun showPaymentMethodDialog(amount: Long, formattedAmount: String, basketId: String? = null) {
val tipsManager = TipsManager.getInstance(activity)
val routing = PaymentRoutingCore.determinePaymentRoute(tipsManager.tipsEnabled)
- val intent = routing.buildIntent(activity, amount, formattedAmount, checkoutBasketJson)
+ val intent = routing.buildIntent(activity, amount, formattedAmount, basketId)
activity.startActivityForResult(intent, REQUEST_CODE_PAYMENT)
}
diff --git a/app/src/main/java/com/electricdreams/numo/payment/PaymentRoutingCore.kt b/app/src/main/java/com/electricdreams/numo/payment/PaymentRoutingCore.kt
index 46ba4622..c659bba9 100644
--- a/app/src/main/java/com/electricdreams/numo/payment/PaymentRoutingCore.kt
+++ b/app/src/main/java/com/electricdreams/numo/payment/PaymentRoutingCore.kt
@@ -19,7 +19,7 @@ object PaymentRoutingCore {
context: Context,
amount: Long,
formattedAmount: String,
- checkoutBasketJson: String?
+ basketId: String?
): Intent {
val targetClass = when (targetActivity) {
TargetActivity.TIP_SELECTION -> TipSelectionActivity::class.java
@@ -28,9 +28,7 @@ object PaymentRoutingCore {
return Intent(context, targetClass).apply {
putExtra(PaymentRequestActivity.EXTRA_PAYMENT_AMOUNT, amount)
putExtra(PaymentRequestActivity.EXTRA_FORMATTED_AMOUNT, formattedAmount)
- checkoutBasketJson?.let {
- putExtra(PaymentRequestActivity.EXTRA_CHECKOUT_BASKET_JSON, it)
- }
+ basketId?.let { putExtra(PaymentRequestActivity.EXTRA_SAVED_BASKET_ID, it) }
}
}
}
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 23aac4cf..e93c1760 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -1,17 +1,18 @@
+
Numo
Open navigation drawer
Close navigation drawer
Manage Catalog Items
-
+
Cashu Payment Service
NDEF Tag Type 4
-
+
Cashu payment request QR code
-
+
Back
NFC Balance Check
Check Balance
@@ -26,7 +27,7 @@
Card does not support IsoDep
Invalid NFC tag
-
+
Back
Tips
Allow customers to add a tip when paying. Tip presets will appear as quick-select buttons on the payment screen.
@@ -36,7 +37,7 @@
Add Preset
Reset to Defaults
-
+
%1$d%%
Add Tip Preset
Enter percentage (1-100)
@@ -57,7 +58,7 @@
Reset
Presets reset to defaults
-
+
Close
Order Total
Would you like to add a tip?
@@ -70,7 +71,7 @@
Back
Confirm
-
+
Close
Share
Pay
@@ -94,7 +95,7 @@
Share Payment Request
-
+
%1$s received.
View Details
Close
@@ -108,13 +109,13 @@
No apps available to share this token
Transaction details not available
-
+
Bitcoin POS with Cashu ecash
Accept Bitcoin payments instantly with zero fees using Cashu ecash technology.
By continuing, you agree to our Terms of Service and Privacy Policy
Get Started
-
+
Set Up Your Wallet
Choose how you would like to get started
Create New Wallet
@@ -122,7 +123,7 @@
Restore from Backup
Use your existing seed phrase
-
+
Back
Restore Wallet
Enter your 12-word seed phrase
@@ -136,7 +137,7 @@
Please paste a valid 12-word seed phrase
Seed phrase pasted
-
+
Creating your wallet...
Generating seed phrase...
Setting up default mints...
@@ -145,11 +146,11 @@
Fetching mint information...
This will only take a moment
-
+
Searching for backup...
Checking Nostr relays for your mint configuration
-
+
Back
Your Mints
Backup Found
@@ -163,11 +164,11 @@
Restore Wallet
Continue
-
+
Restoring Wallet
Initializing restore...
-
+
Wallet Restored
Recovered %1$d sats
Total balance: %1$d sats
@@ -176,11 +177,11 @@
BALANCE BY MINT
Enter Wallet
-
+
Error creating wallet: %1$s
Error initializing wallet: %1$s
-
+
Important Warning
Restoring from a seed phrase will replace your current wallet. Make sure you have backed up your current seed phrase before proceeding.
Enter your 12-word seed phrase
@@ -211,7 +212,7 @@
Restore failed: %1$s
-
+
Back
Seed Phrase
Keep this secret!
@@ -221,12 +222,38 @@
Copy to Clipboard
Make sure to clear your clipboard after pasting
-
+
Terms of Service
- NUMO WALLET - TERMS OF SERVICE
1. ACCEPTANCE
By using this wallet application, you agree to these terms.
2. NATURE OF SERVICE
This is a self-custodial Bitcoin wallet using Cashu ecash technology. You are solely responsible for your funds and seed phrase.
3. SEED PHRASE
Your 12-word seed phrase is the ONLY way to recover your wallet. Never share it. Store it securely offline. We cannot recover lost seed phrases.
4. NO WARRANTY
This software is provided "as is" without warranty of any kind. Use at your own risk.
5. LIMITATION OF LIABILITY
We are not liable for any loss of funds, whether through bugs, user error, or third-party mint failures.
6. ECASH MINTS
Ecash tokens are held by third-party mints. These mints may fail or become unavailable. Diversify across multiple mints.
7. PRIVACY
This wallet does not collect personal data. Transactions are processed through ecash mints which may have their own privacy policies.
8. UPDATES
These terms may be updated. Continued use constitutes acceptance.
Last updated: November 2024
+ NUMO WALLET - TERMS OF SERVICE
+
+1. ACCEPTANCE
+By using this wallet application, you agree to these terms.
+
+2. NATURE OF SERVICE
+This is a self-custodial Bitcoin wallet using Cashu ecash technology. You are solely responsible for your funds and seed phrase.
+
+3. SEED PHRASE
+Your 12-word seed phrase is the ONLY way to recover your wallet. Never share it. Store it securely offline. We cannot recover lost seed phrases.
+
+4. NO WARRANTY
+This software is provided "as is" without warranty of any kind. Use at your own risk.
+
+5. LIMITATION OF LIABILITY
+We are not liable for any loss of funds, whether through bugs, user error, or third-party mint failures.
+
+6. ECASH MINTS
+Ecash tokens are held by third-party mints. These mints may fail or become unavailable. Diversify across multiple mints.
+
+7. PRIVACY
+This wallet does not collect personal data. Transactions are processed through ecash mints which may have their own privacy policies.
+
+8. UPDATES
+These terms may be updated. Continued use constitutes acceptance.
+
+Last updated: November 2024
Close
-
+
Back
Developer Settings
Debugging
@@ -234,7 +261,7 @@
Start the onboarding flow again
⚠️ Developer settings are for testing and debugging purposes only. Use with caution.
-
+
Scan to Pay
Contactless payment
Cashu
@@ -242,7 +269,7 @@
Waiting for NFC...
Cancel
-
+
Back
Top Up
Enter Cashu Token
@@ -266,7 +293,7 @@
An unexpected error occurred: %1$s
No saved PIN available
-
+
Scan NFC Card
Ready to import proofs
Scan Card Again
@@ -274,11 +301,11 @@
Processing Import
Importing proofs...
-
+
Enter PIN
PIN
-
+
Please enter an amount first
PIN-based rescan not supported in this build
Insufficient funds on card
@@ -289,11 +316,11 @@
Satocash Card Error: %1$s (SW: 0x%2$04X)
An unexpected error occurred: %1$s
-
+
OK
or
-
+
Back
Withdraw
Available Balance
@@ -309,7 +336,7 @@
1000
Continue
-
+
Invalid mint URL
Please enter a Lightning invoice
Wallet not initialized
@@ -318,7 +345,7 @@
Please enter a valid amount
Error: %1$s
-
+
Back
Confirm Withdrawal
Withdraw from %1$s
@@ -330,7 +357,7 @@
The fee reserve will be returned if the actual Lightning fee is lower.
Processing withdrawal...
-
+
Invalid melt quote data
Wallet not initialized
Payment failed: Invoice not paid
@@ -352,7 +379,7 @@
Lightning Invoice
-
+
%1$s sent.
To %1$s
Lightning
@@ -360,7 +387,7 @@
Success background
Success
-
+
Activity
PAYMENT HISTORY
No payment history
@@ -422,7 +449,7 @@
Restoring...
+0 sats
-
+
ITEMS CATALOG
No items in catalog
Manage your product catalog for quick checkout.
@@ -430,7 +457,7 @@
Manage
Clear All Items
-
+
Back
Items
Add item
@@ -441,7 +468,7 @@
Import from CSV
Clear All Items
-
+
Back
Add Item
Save
@@ -509,13 +536,13 @@
Cancel
-
+
Drag to reorder
Item image
No image
More
-
+
Item image
No image
Decrease quantity
@@ -524,7 +551,7 @@
Add
Creates a new item with this variation
-
+
Back
Checkout
Search
@@ -557,21 +584,21 @@
Save Basket
Enter a name (optional)
-
+
Save Basket
Give your basket a name to find it easily later.
Table 5, John order, etc.
Leave empty to use default name
Save Basket
-
+
Rename Basket
Update the name for this basket.
Enter new name
Save Changes
Editing: %1$s
-
+
Back
Saved Baskets
No saved baskets
@@ -585,7 +612,7 @@
Delete Basket
Delete \"%1$s\"? This cannot be undone.
-
+
Back
Order History
No archived orders
@@ -602,12 +629,12 @@
View Payment
-
+
Save
Delete
Confirm
-
+
Basket Names
Create preset names for quick basket saving.\nPerfect for restaurant tables or customer orders.
PRESET NAMES
@@ -617,7 +644,7 @@
Examples: Table 1, Table 2, VIP, Takeout, John\'s Order
Clear All Names
-
+
Add Preset Name
Create a quick-select name for baskets
e.g. Table 1
@@ -638,17 +665,17 @@
Remove all preset names? This cannot be undone.
Clear All
-
+
Please enter a name
This name already exists
-
+
- %1$d item
- %1$d items
-
+
Withdrawals
Auto-Withdraw
Automatically send funds to your Lightning wallet when balance exceeds a threshold
@@ -695,27 +722,27 @@
Auto-Withdraw
Error Details
-
+
Withdrawals
Auto-withdraw settings and history
-
+
Manual Withdraw
Withdraw Now
Send funds to Lightning invoice or address
Select Mint
No balance available to withdraw. Add funds to your wallet first.
-
+
Balance
-
+
Select Mint
Choose which mint to withdraw from
Mint icon
Select this mint
-
+
Lightning Invoice
Paste an invoice to pay instantly
Lightning Address
@@ -727,12 +754,12 @@
Send To
Available: %1$s
-
+
Scan QR Code
Point camera at QR code
Camera permission is required to scan QR codes
-
+
Mints
Total Balance
Your Mints
@@ -766,7 +793,7 @@
- %1$d mints
-
+
Lightning Mint
All Mints
Balance
@@ -774,16 +801,16 @@
Set as Lightning Mint
Lightning mint updated
-
+
Mint icon
Selected as Lightning mint
-
+
Info
Delete
Add Mint
-
+
Mint Details
About
Announcement
@@ -805,4 +832,5 @@
Version: %1$s
Software: Unknown
Version: Unknown
-
+ Save or update your basket before checking out
+
\ No newline at end of file
diff --git a/app/src/test/java/com/electricdreams/numo/payment/PaymentRoutingCoreTest.kt b/app/src/test/java/com/electricdreams/numo/payment/PaymentRoutingCoreTest.kt
index d8840da8..d12880d1 100644
--- a/app/src/test/java/com/electricdreams/numo/payment/PaymentRoutingCoreTest.kt
+++ b/app/src/test/java/com/electricdreams/numo/payment/PaymentRoutingCoreTest.kt
@@ -34,13 +34,13 @@ class PaymentRoutingCoreTest {
val decision = PaymentRoutingCore.RoutingDecision(PaymentRoutingCore.TargetActivity.PAYMENT_REQUEST)
val context: Context = ApplicationProvider.getApplicationContext()
- val intent = decision.buildIntent(context, amount = 42L, formattedAmount = "42 sats", checkoutBasketJson = "{}")
+ val intent = decision.buildIntent(context, amount = 42L, formattedAmount = "42 sats", basketId = "basket-123")
val component = intent.component
assertNotNull(component)
assertEquals(ComponentName(context, PaymentRequestActivity::class.java), component)
assertEquals(42L, intent.getLongExtra(PaymentRequestActivity.EXTRA_PAYMENT_AMOUNT, -1))
assertEquals("42 sats", intent.getStringExtra(PaymentRequestActivity.EXTRA_FORMATTED_AMOUNT))
- assertEquals("{}", intent.getStringExtra(PaymentRequestActivity.EXTRA_CHECKOUT_BASKET_JSON))
+ assertEquals("basket-123", intent.getStringExtra(PaymentRequestActivity.EXTRA_SAVED_BASKET_ID))
}
}