It was observed that sending the receipt to the server right after
performing the StoreKit purchase caused issues due to the receipt not
being completely ready when it got sent, causing rejections by the
server.
This commit, in conjuction with backend changes, implements purchase
verification through transaction IDs directly.
Testing
--------
PASS
Device: iPhone 13 Mini (physical device)
iOS: 17.3.1
Damus: This commit
damus-api: `a878da5598a9344a4d351f9a9da16712ce0615b7`
Setup:
- Local server Setup
- Local testing mode on Damus developer settings
- Clean Sandbox account (Create new sandbox account if clearing purchase history does not work)
- Fresh DB
- App is closed before starting.
- Run app on release target
- MOCK_VERIFY=false on server, with all IAP environment correctly setup
Steps:
1. Make Purple IAP purchase. Make sure that:
- Server logs indicate `/apple-iap/transaction-id` is being hit and returning HTTP 200. PASS
- No errors appear on the iOS side. After purchase the welcome sheet should appear, and then the account info. PASS
Part of: https://github.com/damus-io/damus/issues/2036
Changelog-Fixed: Fix in-app purchase issue that would trigger an error on purchase before confirming the account information.
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
Signed-off-by: William Casarin <jb55@jb55.com>
Nothing has changed, but we need to submit a new minor version with
the subscription products since we missed those in the 1.7 appstore
release.
Signed-off-by: William Casarin <jb55@jb55.com>
This should not be visible to end-users on normal circumstances, but we
should regardless show an error message if something goes wrong with the
IAP receipt verification, to prompt them to contact support.
Testing
-------
PASS
Device: iPhone simulator
iOS: 17.2
Damus: This commit
damus-api: d3801376fa204433661be6de8b7974f12b0ad25f
Setup:
- Local Testing server
- Xcode StoreKit environment
Steps:
1. Set MOCK_VERIFY to false on the server (that means receipt verification will fail on Xcode environment)
2. Try to make IAP purchase. Error should appear on UI
3. Set MOCK_VERIFY to true on the server and restart StoreKit environment (Receipt validation will always work)
5. Try to make IAP purchase. Onboarding flow should go as normal with no error messages. PASS
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
This commit changes when the Damus Purple onboarding gets triggered. Now
it only shows the onboarding if it is the first time they are purchasing
Damus Purple for a Nostr account. If it is not the first they see the
onboarding, they are directed to a sheet that displays their new account
info (e.g. a renewal)
However, if the website links to `damus:purple:welcome` links, the app
will still show the onboarding. This will be addressed on the
website-side by taking them to `damus:purple:landing` instead when it is
a renewal.
Testing
---------
PASS
Device: iPhone 13 mini (Physical device)
iOS: 17.3.1
Damus: This commit
damus-api: d3801376fa204433661be6de8b7974f12b0ad25f
damus-website: 6bb425e324c318ca474417cbd2b2f8bb74f9505f
Setup:
- iOS configured to use a local test environment
- Local damus-api server and local damus-website server setup and properly configured
- Fresh db (i.e. Delete mdb files before starting)
Coverage:
1. LN flow with switching to the app instead of clicking "continue". PASS
a. Ensure that first purchase will result in onboarding being shown. PASS
b. Ensure that second purchase will result in onboarding NOT being shown (User should be taken to the account info screen). PASS
c. Restart app completely. Ensure third purchase will result in onboarding NOT being shown (only account info screen). PASS
2. LN flow with clicking continue (Since website changes are not ready, we will simulate them by manually generally appropriate "continue" URL QR codes)
a. Note: Clear DB again before starting this portion, and completely restart app.
b. Ensure that first purchase will result in onboarding being shown. PASS
c. Ensure that second purchase (with continue URL manually hard-coded to `damus:purple:landing`) results in account info being shown with updated info. PASS
d. Restart app completely and repeat test 2(c). PASS
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
This commit adds Damus Purple expiry notification support.
How it works: Whenever the app initiates or enters the foreground, it
checks the user's account expiry, and calculates what notifications to
display (It is functional, not imperative, to better match how
the notifications view works)
The notification handlers work the same as every other notification
handler for Nostr events. However, local iOS notifications were not
implemented to maintain these reminders more discreet.
Current limitations:
- Notifications cannot be dismissed
- Notifications are dismissed only when Damus Purple is extended
- After making a purchase, notifications are not dismissed right away
- Bell icon with purple badge shows up on every app restart if user's account is expired
Testing
-------
Device: iPhone 13 Mini
iOS: 17.3.1
Damus: This commit
damus-api: d3801376fa204433661be6de8b7974f12b0ad25f
Setup:
- Local servers Setup
- Debug endpoints enabled for changing expiry date on the fly
Coverage:
1. Expired account
1. Starting the app on home screen shows bell icon with purple badge. PASS
2. 4 notifications appear on notifications view (7,3,1,0 days to expiry). PASS
3. Notifications appear in correct chronological order. PASS
4. Notifications look consistent in appearance. PASS
5. Expiry notifications' text size follows text size settings. PASS
6. Clicking on notification CTA takes user to account info page. PASS
2. Non-expired account (set expiry, restart app)
1. No expiry notifications, no bell icon. PASS
3. Expiry in 6 days (set expiry, restart app)
1. Starting the app on home screen shows bell icon with purple badge. PASS
2. Starting the app on the notification screen renders notifications the same way. PASS
3. Only one notification (7 days remaining) appears. PASS
4. Expiry in 2 days. PASS
5. General
1. Clicking bell icon clears away "new notifications" badge. PASS
2. Performance of notifications view does not seem affected. PASS
3. Performance of app on startup does not seem affected. PASS
6. IAP
1. Active IAP + expiry date in 2 days does not trigger reminder notification (Because it is auto-renewed). PASS
Closes: https://github.com/damus-io/damus/issues/1973
Changelog-Added: Notification reminders for Damus Purple impending expiration
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
Automatically show welcome sheet for people who completed a Lightning
checkout but did not click on the "Continue" button
Some people get confused in the last step of the lightning checkout and
they do not click on the "Continue in app", which causes the welcome
screen to not show up and the translation setting to not be setup.
This ticket addresses this issue to ensure they get the welcome screen
in such cases.
This is how it works:
- When they go through the mandatory checkout verification step, the
verify screen registers that there is a checkout in progress (and
which checkout is in progress) on the DamusPurple struct
- Every time the app enters the foreground, it checks for that flag:
- If there are no checkouts in progress, it does nothing
- If there is one or multiple checkouts in progress, it checks the
checkout status for those. If any checkout is freshly completed and
successful, and the account is now active, it shows the welcome
screen and onboarding
- Once the welcome screen is shown, those checkouts are marked
internally as handled (so that we only show it once)
===========
Testing
===========
PASS
Damus: This commit
Device: iPhone 13 Mini (real physical device)
iOS: 17.3.1
damus-api: 044150fedddc5ba4135a80579e41e9c1c5743fc0
damus-website: af8089128159e25df31141be624b4090c66d6ddf
Setup:
- Local test Setup
- Clean db before starting
PART 1: LN flow without clicking on the link
---------------------------------------------
Steps:
1. Go through the normal LN flow, EXCEPT at the last step. On the last
step, instead of clicking "continue in the app", just switch to the
app.
2. Ensure the welcome screen and onboarding shows up, and the purple
screen updates to show account info. PASS
3. Switch out and into the app again. Welcome screen should NOT show up
again. PASS
4. Close the app completely and open the app again. Purple welcome
screen should NOT show up again. PASS
PART 2: Normal LN flow
---------------------------------------------
Steps:
1. Reset the entire test setup
2. Go through the normal LN flow (this time click on "continue in app").
The welcome screen should show up without issues or glitches. PASS
PART 3: Crazy flow with multiple incomplete checkouts
---------------------------------------------
(This is to simulate a confused user who accidentally opens multiple new
checkouts, but in the end only completes one of them)
Steps:
1. Reset the entire test setup
2. Open 5 LN checkouts and verify npub with all of them.
3. Only pay one of those checkouts (to make it more confusing, don't pay
the first or last one, but a random one in between)
4. Go to the app directly (Do not use the link, just go directly to the app)
Related: https://github.com/damus-io/damus/issues/1892
Changelog-Fixed: Fix welcome screen not showing if the user enters the app directly after a successful checkout without going through the link
Closes: https://github.com/damus-io/damus/issues/2021
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
Link: 20240223212945.37384-2-daniel@daquino.me
Signed-off-by: William Casarin <jb55@jb55.com>
Apple In-app purchases do not respond immediately. This commit adds a
loading spinner element, to improve the UX and avoid the appearance of a
broken/unresponsive element.
Testing
--------
Device: iPhone 15 simulator
iOS: 17.2
Damus: This commit
Setup:
- Using the sandbox environment
- No tx to start with
- IAP experimental support enabled on developer settings in Damus
Steps:
1. Click "purchase" in any option on the damus purple screen
2. It should show a loading spinner while the purchase action is loading. PASS
3. On the confirmation screen, cancel the purchase.
4. Purchase buttons should show again. PASS
Closes: https://github.com/damus-io/damus/issues/2020
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
Link: 20240223212945.37384-1-daniel@daquino.me
Signed-off-by: William Casarin <jb55@jb55.com>
This patch adds a new ProxyView which is added to the Event Shell
whenever an event has the proxy tag. It includes images of the protocol
logos that are listed in the NIP docs.
Reviewed-by: William Casarin <jb55@jb55>.com
Link: 20240205032400.7069-1-ericholguin@apache.org
Signed-off-by: William Casarin <jb55@jb55.com>
This commit adapts the functionality around login/logout with relation
to Damus Purple In-App purchases (IAP). Due to (apparent) limitations on
Renewable subscription In-app purchases (It seems that there can only be
one active IAP subscription per device or Apple ID), these changes add
support for only one IAP subscription at a time.
To prevent confusion, a customer who logs out and logs into a separate
account will see a message indicating the limitation. Any other Nostr
account won't be able to manage IAP on a device that contains an IAP
registered to a different user.
To make this feature possible, the following changes were made to the
code:
1. IAP purchases are now associated with an account UUID. This account
UUID is generated by the server. Each npub gets one and only one UUID
for this purpose.
2. This UUID is used to determine which npub owns the IAP on the device.
It is used as the source of truth when determining whether a
particular Purple account is manageable on a device or not
3. `DamusPurple` was changed to adhere to a new IAP flow API design
changes. Previously, the client would create an (inactive) account,
and then send the IAP receipt to the server for activation. Now, the
client fetches the npub's UUID from the server, associates it with an
IAP during purchase, and sends the IAP receipt to the server. The
server will then bump the expiry (if it's a renewal) or create a new
active account (if it's the first time).
4. Several changes were made to the StoreKit handling code to improve
handling:
a. The `DamusPurple.StoreKitManager` class now records all purchased
product updates, and sends them to the delegate each time the
delegate is updated. This helps ensure we do not miss purchased
product updates regardless of when and if `DamusPurpleView` is ever
instantiated.
b. `DamusPurple.StoreKitManager` is now used by `DamusPurple` in a
singleton pattern via `DamusPurple.StoreKitManager.standard`. This
helps maintain the local purchase history consistent (and avoid
losing such data) after `DamusState` or its `DamusPurple` are
destroyed and re-initialized.
c. Added logs (using the logger) to help us debug/troubleshoot
problems in the future
5. Changed the views around DamusPurple, to only show IAP
purchase/management options if applicable to a particular account. It
also shows instructive messages in other scenarios.
Testing
-------
damus: This commit
damus-api: d3956ee004a358a39c8570fdbd481d2f5f6f94ab
Device: iPhone 15 simulator
iOS: 17.2
Setup:
- Xcode (local) StoreKit environment
- All StoreKit transactions deleted before starting
- Running `damus` app target (which contains test StoreKit products)
- Local damus-api server running with `npm run dev` and
`MOCK_VERIFY=true` to disable real receipt verification
- Damus setup with experimental IAP support enabled, and Purple
environment set to "Test (local)" (localhost)
- Two `nsecs` readily available for account switching
- Clean DB (Delete db files before starting)
Steps:
1. Open the app and sign in to the first account
2. Go to Damus Purple screen. Marketing screen with buttons to purchase
products should be visible. PASS
3. Buy a product and monitor server logs as well as the screen.
a. IAP confirmation dialog should appear. PASS
b. After confirmation, server logs should show a receipt was sent
IMMEDIATELY and the response should be an HTTP 200. PASS
c. The welcome and onboarding screens should appear as normal. PASS
d. Once the onboarding sheet goes away, the Purple screen should now
show the account information. PASS
e. The account information should be correct. PASS
f. Under the account information, there should be a "manage" button. PASS
4. Click on "manage" and verify that the iOS subscription management
screen appears. PASS
5. Now log out and sign in to the second account
6. Go to Damus Purple screen.
a. Marketing screen should be visible. PASS
b. There should be no purchase buttons. instead, there should be a
message indicating that there can only be one active subscription
at a time, and that the app is unable to manage subscription for
this second acocunt. PASS
7. Log out and sign in to the first account. Go to the Purple screen.
a. Account info with the manage button should be visible like before. PASS
8. Through Xcode, delete transactions, and restart the app. This will
simulate the case where the user bought the subscription externally.
9. Go to the Purple screen.
a. Account info should be visible and correct. PASS
b. Below the account info, there should be a small note telling the
user to visit the website to manage their billing. PASS
Closes: https://github.com/damus-io/damus/issues/1815
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
Reviewed-by: William Casarin <jb55@jb55.com>
Signed-off-by: William Casarin <jb55@jb55.com>
This commit moves most of StoreKit-specific logic that was embedded into
DamusPurpleView and places it into a new PurpleStoreKitManager struct,
to make code more reusable and readable by separating view concerns from
StoreKit-specific concerns.
Most of the code here should be in feature parity with the previous
behavior. However, a few logical improvements were made alongside this
refactoring:
- Improved StoreKit transaction update monitoring logic: Previously the
view would stop listening for purchase updates after the first update.
However, I made the program continuously listen for purchase updates,
as recommended by Apple's documentation
(https://developer.apple.com/documentation/storekit/transaction/3851206-updates)
- Improved/simplified logic around getting extra information from the
products: Information and the handling of product information was
spread in a few separate places. I incorporated those bits of
information into central and uniform interfaces on DamusPurpleType, to
simplify logic and future changes.
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
Reviewed-by: William Casarin <jb55@jb55.com>
Signed-off-by: William Casarin <jb55@jb55.com>
This refactoring commit splits several view blocks from DamusPurpleView
into separate files.
- New view structs were defined within the DamusPurpleView namespace, to
avoid polluting the global namespace
- No logical changes were made. The functionality should have stayed
equivalent
- Changes were made conservatively, and as semantically as possible, to
make the code easier to work with.
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
Signed-off-by: William Casarin <jb55@jb55.com>
This refactoring commit moves primitive, low complexity, helper views
from DamusPurpleView into a separate file, to reduce complexity on
DamusPurpleView.swift.
Although functions were changed into View structs, no logical changes
were made. (New version is functionally equivalent)
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
Reviewed-by: William Casarin <jb55@jb55.com>
Signed-off-by: William Casarin <jb55@jb55.com>
This is a purely non-functional refactor of DamusPurpleView consisting
only of code mark section comments, and re-ordering for better
organization and to facilitate further refactoring.
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
Signed-off-by: William Casarin <jb55@jb55.com>
This commit adds a developer setting that allows the use of a custom
host for testing. This was added to allow testing on real devices
without the need for pushing changes into staging.
========
Testing
========
Test 1: Production not affected
-------------------------------
PASS
Device: iPhone 13 Mini
iOS: 17.3
Damus: This version
Steps:
1. Run app on a device logged into a real Damus Purple account
2. Scroll down the home feed. Make sure that other Purple members still show up with a star next to their profile. PASS
3. Go to the Damus Purple screen. Ensure that account info shows up and is correct. PASS
4. Ensure auto-translations appear. PASS
Test 2: Check custom test URL
-----------------------------
PASS
(Continued from test 1)
1. Run local damus-api and damus-website on the same computer
2. Change developer purple env setting to local test
3. Set the host url to the local IP address of the test server
4. Go through LN purchase flow. Ensure it works correctly. PASS
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
Reviewed-by: William Casarin <jb55@jb55.com>
Signed-off-by: William Casarin <jb55@jb55.com>
This was broken by the recent nip19 changes, let's fix it again
Fixes: cb4adf06f1 ("nip19: added swift enums")
Changelog-Fixed: Fix nostrscripts not loading
Signed-off-by: William Casarin <jb55@jb55.com>
otherwise there is contention and tends to crash
Fixes: f06b882139 ("purple: consolidate UserBadgeInfo with Account")
Changelog-Fixed: Fix crash when accessing cached purple accounts
Signed-off-by: William Casarin <jb55@jb55.com>
Also doing this so I can add a changelog entry
Changelog-Changed: Disable inline text suggestions on 17.0 as they interfere with mention generation
Signed-off-by: William Casarin <jb55@jb55.com>
Minimum width is not needed since we rely on maxWidth set to .infinity
to fill the screen bounds.
Lightning-address: kernelkind@getalby.com
Signed-off-by: kernelkind <kernelkind@gmail.com>
Reviewed-by: William Casarin <jb55@jb55.com>
Link: 20240131165008.61990-1-kernelkind@gmail.com
Signed-off-by: William Casarin <jb55@jb55.com>
The purple membership badge should be displayed as compact view
everywhere except the user's profile.
Lightning-address: kernelkind@getalby.com
Changelog-Fixed: Hide member signup date on reposts
Signed-off-by: kernelkind <kernelkind@gmail.com>
Reviewed-by: William Casarin <jb55@jb55.com>
Signed-off-by: William Casarin <jb55@jb55.com>