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)
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.
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.
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
- 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.
This removes `FeatureFlags.giftBadgeReceiving` and
`FeatureFlags.giftBadgeSending`, replacing them with
`RemoteConfig.canReceiveGiftBadges` and
`RemoteConfig.canSendGiftBadges`.
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.
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.
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.
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
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.
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.
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.
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
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.