Commit Graph

84 Commits

Author SHA1 Message Date
Evan Hahn
ec78cc3a0a
Hide Apple Pay UI when the server disallows your region
_I recommend reviewing this with whitespace changes disabled._

The server's `global.donations.apayDisabledRegions` is a list of regions
(phone number prefixes) where Apple Pay is disallowed. This respects
that.
2022-11-10 16:19:47 -06:00
Sasha Weiss
242dfd2bce
Add all JobQueues to environment, via a wrapper
Currently, only some `JobQueue` types are initialized during startup
(as part of `Environment`, or `SSKEnvironment`). Initialization is
required, however, for a `JobQueue` type to restart any latent jobs.
That means that, for example, a durable `SendGiftBadge` job that failed,
and should be reattempted at a later date, will not in fact be restarted
since no `SendGiftBadgeJobQueue` will be initialized at launch.

This change adds `SSKJobQueues` and `SignalMessagingJobQueues` types,
which are intended to be singletons that hold within them a singleton
job queue for each of our `JobQueue` types. These wrappers are added to
`SSKEnvironment` and `Environment` (from SignalMessaging) respectively,
ensuring that all the `JobQueue`s they contain are initialized as part
of environment setup. The wrappers also avoid the need to add a new
property to the (already large) environment types for each new future
`JobQueue`.

This change also updates all existing call sites that accessed a
`JobQueue` from an environment object, to direct that access now through
the wrapper type.
2022-11-09 14:08:44 -06:00
Evan Hahn
513ba3863d Crash, don't throw, if donation receipt generation fails
_I recommend reviewing this with whitespace changes disabled._

We need to generate a "receipt request" before doing a donation. This is
is entirely on-device and is unlikely to fail, but it technically could.

Previously, `generateReceiptRequest` would throw if this failed. We did
a bad job handling these failures (in some cases, we just ignored the
failures completely!).

Because this represents such a catastrophic failure, we'd _like_ this to
crash the app so we can investigate it more urgently.
2022-11-09 11:49:30 -06:00
Evan Hahn
198fc1784a
Consolidate one-time & monthly donation screens
Previously, one-time donations were on one screen and monthly donations
were on another. Now, they're on a single screen with a picker.

Most of the interesting changes are in `DonateViewController`.

Other things of note:

- There are some new TODOs here for existing bugs I didn't fix. For
  example, one-time donations don't do so well if there are any problems
  at all.

- Even though we only support Apple Pay, there's code that alludes to
  additional payment methods. For example,
  `DonateChoosePaymentMethodSheet`. We'll expand on this in the future.

- Users should only be able to select currencies that the server
  supports. For example, you shouldn't be able to select EUR if the
  server doesn't support euros. This wasn't working correctly before,
  but is fixed here.

  We already fixed one part, where users could change to an unsupported
  currency (see dab02f30ae). However, if
  your _default_ currency is unsupported and you didn't change it,
  that's no good. This is unlikely for most users but could happen.

  I fixed this by changing it from (effectively)
  `Locale.current.currencyCode ?? "USD"`, which might not be supported,
  to a preference list, choosing the first one the server likes.

- I skip animations if the Reduce Motion setting is enabled.

- On the donation screen, the logic for the preview badge has changed
  slightly. If you already have a badge, we'll always use that.
2022-11-08 16:27:50 -06:00
Evan Hahn
d96d7d6160 DonationUtilities doesn't need to be an NSObject
This change should have no user impact.
2022-10-28 15:51:26 -07:00
Evan Hahn
4675434c6c Add FiatMoney type
This change should have no user impact.

We commonly pair a currency, like USD, and an amount, like 1.23. This
adds the `FiatMoney` struct. It's a simple struct with two fields.

After adding it, I tried to use it everywhere. (It's possible I missed a
spot.)

I think this is a useful change on its own, but it'll be nice for an
upcoming change, too.

See also: [Android's equivalent class][0].

[0]: cb65347bb3/core-util/src/main/java/org/signal/core/util/money/FiatMoney.java (L1)
2022-10-26 17:04:51 -05:00
Evan Hahn
e03461b176 Use "donations" in one-time/monthly donation logs
Previously, we used `[Subscriptions]` which wasn't always right. Now, we
use `[Donations]`.
2022-10-18 09:33:17 -07:00
Evan Hahn
623b1909b2
Prefer Decimal over NSDecimalNumber
This change should have no user impact.

We use `NSDecimalNumber` and `Decimal`. This tries to standardize on
`Decimal` where possible (though the former is still needed in a few
places).

Mostly a mechanical change, though a few little things changed.
2022-10-18 09:43:46 -05:00
Evan Hahn
10a78d6033
Clean up min/max checks for Stripe payments
We check whether a payment amount is too small or too large. This makes
a few changes:

- `isAmountTooLarge` was correct except for [IDR][1], which has a
  larger limit according to [Stripe's docs][0].
- Added docs for `isAmountTooSmall` and `isAmountTooLarge`.
- Updated rounding to use "normal" rounding instead of "banker's
  rounding".

[0]: https://stripe.com/docs/currencies?presentment-currency=US#minimum-and-maximum-charge-amounts
[1]: https://en.wikipedia.org/wiki/Indonesian_rupiah
2022-10-17 13:45:57 -05:00
Evan Hahn
48e87a46ce Add BYN and SLE to list of Stripe currencies
This adds [BYN][] and [SLE][] to the list of currencies, according to
Stripe's docs.

[BYN]: https://en.wikipedia.org/wiki/Belarusian_ruble
[SLE]: https://en.wikipedia.org/wiki/Sierra_Leonean_leone
2022-10-17 11:33:42 -05:00
Evan Hahn
e0ebfac315 Handle decimals in one-time and gift donations
We fetch the cost of gift badges and suggested one-time donation
amounts. For example, gift badges cost $5 and £4.

Previously, we parsed these as `UInt`s. This had two disadvantages:

1. There was no way to represent fractional amounts (like $1.23)
2. It was inconsistent with a lot of our code, which expects `Decimal`s
   or `NSDecimalNumber`s

This changes these to use `Decimal` instead.

Tested this by:

- Adding some automated tests
- Making a one-time donation (with a non-default currency)
- Sending a gift badge (with a non-default currency)
- Checking donation receipts

We also had some hard-coded presets for one-time donations, which were
unused. I removed these.
2022-10-17 09:18:05 -07:00
Evan Hahn
6ea3532540 Use set, not array, for zero-decimal currency list
We check if a currencies are in a list ([example][0]). We can speed this
up, and better clarify intent, if we use a `Set` instead of an array.

[0]: 35bc42d159/SignalMessaging/Subscriptions/DonationUtilities.swift (L72)
2022-10-14 10:08:00 -07:00
Evan Hahn
370ff654e7
Change license to AGPL
Change license to AGPL

This commit:

- Updates the `LICENSE` file

- Start every file with something like:

      // Copyright YEAR_FIRST_PUBLISHED Signal Messenger, LLC
      // SPDX-License-Identifier: AGPL-3.0-only

---

First, I removed existing license headers with this Ruby 3.1.2 script:

    require 'set'

    EXTENSIONS_TO_CHECK = Set['.h', '.hpp', '.cpp', '.m', '.mm', '.pch', '.swift']

    same = 0
    different = 0

    all_files = `git ls-files`.lines.map { |line| line.strip }
    all_files.each do |relative_path|
      if relative_path == 'Pods'
        next
      end

      unless EXTENSIONS_TO_CHECK.include? File.extname(relative_path)
        next
      end

      path = File.expand_path(relative_path)

      contents = File.read(path)
      new_contents = contents.sub(/\/\/\n\/\/  Copyright .*\n\/\/\n\n/, '')

      if contents == new_contents
        same += 1
      else
        different += 1
      end

      File.write(path, new_contents)
    end

    puts "updated #{different} file(s), left #{same} untouched"

I'm sure this script could be improved, but it worked well enough.

Then, I created `Scripts/lint/lint-license-headers` and ran it to auto-
fix a lot of files. This changed the mode of some files, but I think
that's actually desirable. For example,
`SignalServiceKit/src/Util/AppContext.m` previously had a mode of
`0755/-rwxr-xr-x`, and it's now `0644/-rw-r--r--`.

Then I fixed some stragglers and updated the precommit script.

See [a similar change in the Desktop app][0].

[0]: 8bfaf598af
2022-10-13 08:25:37 -05:00
Max Radermacher
9953f99784 Remove unused donation code 2022-10-12 11:30:39 -07:00
Max Radermacher
6f19522b55
Clarify label for recurring donations 2022-09-23 15:38:34 -07:00
Michelle Linington
4ada6b3a3f Derive bundle ID from an Xcode build setting
See [#5423][5423].

[5423]: https://github.com/signalapp/Signal-iOS/pull/5423
2022-09-21 18:10:42 +00:00
Evan Hahn
cef1d330cd Improve logging for donations
This adds logging in various places to (hopefully) make it easier for us
to diagnose future problems.
2022-08-02 18:19:40 -05:00
Max Radermacher
def8129ac8
Handle expired gift badges
- Show an error when trying to redeem them
- Show an error if your last remaining gift badge expires
2022-07-27 13:15:58 -07:00
Max Radermacher
cc0cd24f88 Improve validation for incoming gift messages
- Drop gift messages sent to groups.
- Drop gift messages if the receipt credential presentation isn’t valid.

- Allow any level in a gift message, for future compatibility.
2022-07-26 09:28:48 -07:00
Evan Hahn
028d97c2cb
Use remote config for gift badges
This removes `FeatureFlags.giftBadgeReceiving` and
`FeatureFlags.giftBadgeSending`, replacing them with
`RemoteConfig.canReceiveGiftBadges` and
`RemoteConfig.canSendGiftBadges`.
2022-07-20 08:50:58 -05:00
Max Radermacher
a7ed8a2847 Cache gift badge endpoint for conversation view
If there are multiple requests, they’ll be merged into one. If a request
has already succeeded, it will be used until the app relaunches. If a
request fails, the next caller will kick off a new request.
2022-07-19 15:01:36 -07:00
Max Radermacher
29507343ff Remove unused subscription & badge code 2022-07-19 09:45:01 -07:00
Max Radermacher
0fc1d064d8 Don’t fetch avatar when checking current badges 2022-07-19 09:44:46 -07:00
Evan Hahn
64462ac1b0 Save a receipt when you send someone a badge
This commit:

1. Adds a new donation receipt type: gifts.
2. Saves receipts when sending someone a gift.

The first part was the main challenge. Previously, donation receipts had
logic like this (pseudocode):

     def getReceiptType(self):
       if self.subscriptionLevel:
         return "subscription"
       else:
         return "one-time"

Now, we explicitly encode the type and have logic to handle "legacy"
receipts that don't have a type encoded.
2022-07-08 13:17:30 -05:00
Evan Hahn
f5556d1857
Use durable job for sending badge gifts
Previously, sending a gift badge was not a durable operation, which
meant that crashes/failures could cause users to have their payment
methods charged without actually sending the badge.

Now, the flow is split up into two steps: non-durable parts before the
charge is attempted, and durable parts afterward.

The high-level flow is:

1. Prepare the payment, which involves a couple of repeatable network
   requests.
2. Enqueue a job with the prepared payment, that:
   1. Charges the payment method (idempotently)
   2. Requests a receipt credential (idempotently)
   3. Enqueues a gift message, and optionally a text message
3. When the job completes, open the conversation in the UI.
2022-07-01 17:10:31 -05:00
Max Radermacher
f7f66d6436
Add support for redeeming gift badges 2022-07-01 14:07:24 -07:00
Max Radermacher
807ea6e034
Simplify …BadgeIds.contains(_:)
There’s no need to use `allCases` -- we can accomplish the same thing
using the automatically-provided `init(rawValue:)` initializer.
2022-06-28 17:15:14 -07:00
Evan Hahn
f4714b1a83 Basic support for sending gift badges
If you've got the `giftBadgeSending` flag enabled, you can now send gift
badges to anyone who has the capability.

This commit doesn't complete the feature, though. It is missing:

- Proper durability and error handling (to be addressed separately)
- Saving of receipts
- A few other cleanups
2022-06-22 15:50:51 +00:00
Max Radermacher
c0a85b888b
Add rendering support for Gift Badges
Co-authored-by: Evan Hahn <69474926+EvanHahn-Signal@users.noreply.github.com>
2022-06-22 08:05:25 -07:00
Evan Hahn
a1eb9e3042 Clean up boostCreatePaymentIntent()
This makes a few small changes to `OWSRequestFactory.boostCreatePaymentIntent()`:

- Adds tests
- Rewrites the function in Swift

I made sure I could do a boost (in staging) in the simulator.
2022-06-21 17:33:28 +00:00
Max Radermacher
05ed931b10 Convert boost/gift badge constants to an enum 2022-06-09 15:01:22 -07:00
Evan Hahn
d2836235af Show additional details for subscription charge failures
In c5bdf6c094, we started to show errors
when a subscription failed to renew because of a charge failure.

Now, we show additional text depending on what the charge failure was.
For example, if you had an invalid card number, we show a special string
for that.
2022-05-25 18:17:53 +00:00
Evan Hahn
baddc85564 Add first gift badge screen
This adds the first screen for badge gifting. It lets you see the gift
badge, pick the currency, and advance to the next screen.

It also adds a skeleton for the next screen, so there's somewhere to
advance to, but that screen is unfinished.

All of this is behind disabled flags, so this should have no user
impact.
2022-05-19 14:07:23 +00:00
Evan Hahn
29c0ddf60e Fix violations of SwiftLint's attributes rule
_I recommend reviewing this with whitespace changes disabled._

Most of these were `@objc` being on the same line, which I fixed with [a
silly script][1]—probably could've used `sed` if I were wiser. The rest
were manual fixes.

See [the SwiftLint documentation for this rule][0].

[0]: https://realm.github.io/SwiftLint/attributes.html
[1]: https://gist.github.com/EvanHahn-Signal/d353c93fa269c82b96baca0a1086521f
2022-05-14 09:07:42 -05:00
Evan Hahn
57ea808a3c Simplify Subscription constructor
This is a followup to c5bdf6c094.
2022-05-13 14:08:32 -05:00
Evan Hahn
8721d4e2a5 Skeleton "choose gift badge" screen
It doesn't do anything yet, but an image and some text is there.

This is all behind a feature flag, so this should have no user impact.
2022-05-13 13:25:36 -05:00
Evan Hahn
c5bdf6c094 Improve subscription charge error UI
Currently, when your subscription expires due a charge failure, we
incorrectly tell you that it's due to inactivity. This fixes that, by
telling you about the charge failure.

This is a bit difficult to test on its own, so I:

- Faked out the smallest pieces I could in an effort to test states
  manually
- Created `BadgeErrorSheetState` which is pretty thoroughly tested
2022-05-12 18:28:14 -05:00
Max Radermacher
34767e8beb Increase accepted receipt credential time to 90d
Per IOS-2387.
2022-05-03 10:20:10 -05:00
Max Radermacher
88a28c3457 Fix typos: Reciept → Receipt 2022-04-29 12:14:09 -07:00
Evan Hahn
67ea0d0516 Fix some trivial compiler warnings
This fixes 10 of our Xcode warnings:

- `MessageReactionPicker.swift` was declaring a variable and not using
  it, leading to "Immutable value 'emoji' was never used". I simply
  removed it.
- `Stripe.swift` had a bunch of unnecessary `public`s, which caused
  "'public' modifier is redundant for static property declared in a
  public extension".
- `SubscriptionManager.swift` had an unnecessary `try`, causing "No
  calls to throwing functions occur within 'try' expression".
- `CallService.swift:696` was calling a function and not using its
  result, so I annotated that function with `@discardableResult`.
- `MobileCoinAPI+Configuration.swift` declared a variable with `var`
  that should've used `let`.

Nothing major here, but wanted to find ones that were easy to fix.
2022-04-19 09:28:15 -05:00
Evan Hahn
8b072620a6 Add "Donation Receipts" view 2022-04-11 16:21:12 -05:00
Nora Trapp
05313c3725 Update to LibSignalClient v0.15.0 2022-03-24 11:55:45 -07:00
Dimitris Apostolou
62724cf0be Fix typos 2022-03-18 11:31:06 -07:00
Martin Böttcher
8f9be31ba3 replaced NSLocalizedStringFromAppBundle with OWSLocalizedString 2022-03-08 10:17:25 +01:00
Martin Böttcher
c0adfbfb32 NSLocalizedString is replaced by NSLocalizedStringFromAppBundle (reading strings from the bundle of the main app) for all source files called by an app extension and the localizable files are removed for the app extension targets. 2022-03-07 13:29:06 +01:00
Martin Böttcher
c4f967ef46
removed checks testing for iOS12.0 or newer (#4030) 2022-02-25 17:39:12 +01:00
Evan Hahn
0ffba32ba3 Cancel subscription when deleting account 2022-02-24 16:51:09 -06:00
Nora Trapp
aeaf5d86d6 Don't show subscription megaphone again until your subscription has been expired for at least 2 weeks 2022-02-11 13:09:20 -08:00
Nora Trapp
a2f51dc4a3 Cleanup old feature flags 2022-01-24 10:53:55 -08:00
Nora Trapp
db115606e3 Remove ZKGroup 2022-01-03 12:00:19 -08:00