Commit Graph
40 Commits
Author SHA1 Message Date
Facundo Menzella 6b5c430082 chore: Enable force_unwrapping for SwiftLint (#4820)
* enable force_unwrapping

* avoid force unwrap

* fixed all errors

* remove extra disable
2025-02-26 12:40:49 +01:00
Facundo Menzella fa3ea3264d feat: Add tests for customer center events encoding (#4739) 2025-02-03 12:17:31 +01:00
NachoSoto 2bcd0ce328 5.0.0 changes: support SK2 by default 2024-07-01 14:37:21 -05:00
Cesar de la VegaandNick Kohrn 7a6af74fd3 [EXTERNAL] Fix Typos in ReceiptStrings.swift (#3756) via @nickkohrn (#3760)
Merging #3756 into `main`

Co-authored-by: Nick Kohrn <nickkohrn@users.noreply.github.com>
2024-03-13 15:46:25 +01:00
Mark VillacampaandNachoSoto 84806ad86e Improve sandbox detector for macOS apps (#3549)
Checking for the presence of `Xcode/DerivedData` in the receipt path was
failing for TestFlight builds or if the .app bundle generated by Xcode
was moved from the folder.

Additionally, checking for `MASReceipt/receipt` seems less robust than
simply using compiler directives.

Instead, for macOS apps, we check the environment embedded in the receipt and alternatively check the signature of the app bundle.

---------

Co-authored-by: NachoSoto <NachoSoto@users.noreply.github.com>
2024-01-09 19:12:47 +01:00
32c63cdb46 [SK2] Add support for StoreKit Config files in SK2 (#3365)
When using StoreKit Config file, we do not have access to the
transaction history or subscription status server-side.

To work around this, we introduce a new `StoreKit2Receipt` type that
closely resembles the structure of an SK1 receipt and contains all the
transaction history and subscription status for the customer. It can be
sent in a single request to `/receipt`

---------

Co-authored-by: RevenueCat Git Bot <72824662+RCGitBot@users.noreply.github.com>
Co-authored-by: Distiller <distiller@static.38.39.183.137.cyberlynk.net>
Co-authored-by: Distiller <distiller@static.38.23.40.89.cyberlynk.net>
Co-authored-by: Distiller <distiller@static.38.39.185.65.cyberlynk.net>
Co-authored-by: NachoSoto <NachoSoto@users.noreply.github.com>
2023-12-13 17:00:37 +01:00
NachoSoto 4d95c03798 CI: added workaround for Snapshots in Xcode Cloud (#2857)
See
https://github.com/pointfreeco/swift-snapshot-testing/discussions/553.
Blocked by https://github.com/codecov/uploader/issues/1126.
2023-10-21 11:12:16 -07:00
235a7dcdc1 StoreKit 2: Optionally send JWS tokens instead of receipts to the backend (#3227)
- Added a new option `usesStoreKit2JWS` under `DangerousSettings`. If
enabled, the SDK will send a JWS token instead of a receipt to the
applicable backend endpoints.
- The option must be used in conjunction with `usesStoreKit2IfAvailable`
configuration option.

### ToDo

- [x] Send JWS token when making a purchase in SK2 mode
- [x] Send JWS token when calculating promo offer eligibility
- [x] Update `syncTransactions` to send the latest transaction JWS.
- [x] Add tests

---------

Co-authored-by: RevenueCat Git Bot <72824662+RCGitBot@users.noreply.github.com>
Co-authored-by: Distiller <distiller@static.38.39.183.118.cyberlynk.net>
Co-authored-by: Distiller <distiller@static.198.206.135.125.macminivault.com>
Co-authored-by: Distiller <distiller@static.38.23.39.112.cyberlynk.net>
Co-authored-by: Distiller <distiller@static.38.39.183.58.cyberlynk.net>
Co-authored-by: Distiller <distiller@static.38.39.185.87.cyberlynk.net>
2023-10-16 22:45:29 +02:00
NachoSoto 82435936d9 Xcode 13: remove conditional code (#3147)
Clean up thanks to #2421
2023-09-06 10:59:22 -07:00
NachoSoto c249244a13 ReceiptParser: fixed SPM build (#3144)
This was wrong in #3108. `ProcessInfo+Extensions` was added to the
`ReceiptParser` target, but not when building through SPM. This fixes
that.

We didn't catch this with the `spm-receipt-parser` CI job because that
does a release build.
2023-09-05 11:34:43 -07:00
NachoSoto b59582eed6 Integration Tests: workaround to not lose debug logs (#3108) 2023-08-30 06:10:33 -07:00
NachoSoto 216f1e13c3 PurchasesOrchestrator: disambiguate transactions from the queue (#2890)
This adds tests and a fix for a scenario discovered by @aboedo and
@mcastany.

### Timeline:
- Make a purchase of product_1
- Close app
- Renewals happen in the background
- Subscription expires
- Open app, renewals are in the queue but not processed yet
- Start a purchase of `product_1` (`SKPaymentQueue.add`)
- StoreKit queue shows renewal from step 3 as finished, it's expired
- SDK sees finished transaction in the queue for `product_1`
- SDK sees completion block for `product_1`, considers it a successful
`.purchase`
- SDK posts receipt, `initiation_source: .purchase` 💥
- Backend replies with 422 because StoreKit hasn't processed the
transaction from 6

### Fix:
The fix looks at the date when the completion block was added (the time
`.purchase()` is called).
If the transaction was created _before_ this, we know it didn't come
from the actual purchase, and therefore it should be identified as
`.queue`.
2023-08-07 15:57:10 -07:00
NachoSoto 7f875ae212 Changed Logger to use os_log (#2608) 2023-06-15 15:13:48 -07:00
NachoSoto 4187316738 Encodable.jsonEncodedData: fixed tests on iOS 17 due to inconsistent key ordering (#2607)
A bunch of tests were failing on Xcode 15 due to [a change in
Swift](https://github.com/apple/swift/issues/66466), because we relying
on being able to verify that the encoded `Data` saved to caches is
consistent.
Starting on Swift 5.9 `JSONEncoder` has optimizations that mean we can't
rely on it.

To fix this, this PR does 2 things:
- Changed `JSONEncoder.prettyPrinted` to have `.sortedKeys`
- Changed test to verify the decoded response `struct`, not the raw
encoded `Data`

Note that I didn't make `JSONEncoder.default` use `.sortedKeys` because
of the potential performance impact. `Encodable.prettyPrintedData` is
used in tests like `ReceiptFetcherTests` (which this PR fixes) and for
debug purposes, but `Encodable.encodedJSON` is used everywhere.
2023-06-09 11:30:59 -07:00
NachoSoto 2276d5c2ce AppleReceipt.debugDescription: don't pretty-print JSON (#2564) 2023-05-31 07:11:23 -07:00
NachoSoto e6e79c95a7 AppleReceipt.mostRecentActiveSubscription: performance optimization (#2332)
No need to sort the entire list of subscriptions
2023-03-13 14:19:19 -07:00
NachoSoto a157adfdee EntitlementInfo: add a grace period limit to outdated entitlements (#2288)
This prevents users from keeping an outdated active entitlement forever.
One could start a free trial then block all further requests from the
backend in order to keep that active.

When this check fails, a new warning log is generated:
> WARN: ⚠️ Entitlement is no longer active (expired 2023-02-12 18:15:11
+0000) and it's outside grace period window (last updated 2023-02-12
18:13:31 +0000)
2023-03-01 09:45:29 -08:00
NachoSoto fdd7874deb IntroEligibilityCalculator: fixed logic for subscriptions in same group (#2174)
- Fixes  [TRIAGE-221] for `SK1`.
- Filed `FB11889732` for `SK2`.

### Changes:
- Added 2 `IntroEligibilityCalculatorTests` unit tests to cover this
scenario
- Added 1 `IntroEligibilityCalculatorTests` unit tests to cover the
equivalent of the `testIneligibleForIntroAfterPurchaseExpires`
integration test
- Added 2 `StoreKitIntegrationTests` to cover this scenario (one of them
disabled in SK2)
- Updated `IntroEligibilityCalculator` logic to fix the new tests.
- Refactored `IntroEligibilityCalculatorTests` to avoid duplication and
allow adding new cases more easily.
- Added a few more tests to ensure other cases are correct.

[TRIAGE-221]:
https://revenuecats.atlassian.net/browse/TRIAGE-221?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
2023-01-25 08:40:35 -08:00
NachoSoto b87dfe5b51 Added new ReceiptParser.fetchAndParseLocalReceipt (#2204)
Fixes [CSDK-622].

### Changes:
- Extracted `FileReader` to be able to use it in `ReceiptParser`
- Extracted `MockBundle` to be able to use it in `ReceiptParser`
- Added new `public` `ReceiptParser.fetchAndParseLocalReceipt()`, **only
to `ReceiptParser`** (this is not available through `RevenueCat`).
- Added tests for the new API
- Added new API to `ReceiptParserAPITester`

Initially I thought about moving `ReceiptFetcher` to the
`Receipt|Parser` target. Unfortunately that would require moving a whole
bunch of other dependencies (`OperationDispatcher`, etc, etc).
We have no easy way to create a third "Core" framework in which to place
these common dependencies, so that wouldn't be trivial.

This solution adds a much simpler version of `ReceiptFetcher` that
simply reads from `Bundle.appStoreReceiptURL`. As it's documented there,
under normal circumstances `SKReceiptRefreshRequest` is not required, so
this implementation should work for 99% of use cases, and so this way we
avoid increasing the.codebase complexity unnecessarily.

[CSDK-622]:
https://revenuecats.atlassian.net/browse/CSDK-622?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
2023-01-17 12:35:56 -08:00
NachoSoto e4bbd0528b ReceiptFetcher: refactored implementation to log error when failing to fetch receipt (#2202) 2023-01-16 07:43:34 -08:00
NachoSoto e3f9a68d25 PurchasesReceiptParser: improved documentation to reference default (#2197)
The most obvious way to create `PurchasesReceiptParser` is using
`.init`.
In order to expose that we'd need to `override` `init` from an
`extension` because each (RC / `ReceiptParser`) `target` needs a
different `LoggerType`. However, `NSObject.init` can't be overridden
from an extension.

That's why the API exposes `default` instead. This will make the usage
clearer:

![Screenshot 2023-01-05 at 10 22
16](https://user-images.githubusercontent.com/685609/210852731-4ac7336a-90a9-4fa8-8135-a8bb723a3701.png)
2023-01-12 11:17:54 -08:00
NachoSoto d1b0c46bee PurchasesReceiptParser: added API to parse receipts from base64 string (#2200)
[CSDK-621].


[CSDK-621]:
https://revenuecats.atlassian.net/browse/CSDK-621?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
2023-01-12 10:08:55 -08:00
NachoSoto 0819eca3e8 Restore purchases: post product data when posting receipts (#2178)
Fixes [CSDK-604].

When restore receipts, we don't post any price for products because we
don't know what the user bought.
As a best-effort approach, this parses the `AppleReceipt` and looks for
products that were purchased.


[CSDK-604]:
https://revenuecats.atlassian.net/browse/CSDK-604?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
2022-12-27 10:13:07 -08:00
NachoSoto 6d78f2bffe Created ReceiptParser SPM (#2155)
Finishes [CSDK-17].
Follow up to #2153. Extracted from #2096.

### Changes:
- Created target in `Package.swift`. It can be built separately: `swift
build --target ReceiptParser`
![Screenshot 2022-11-29 at 09 29
10](https://user-images.githubusercontent.com/685609/204600321-9c99958c-a683-4b59-8334-df1cf1235d1d.png)
- Created `ReceiptParser` target in project:
![Screenshot 2022-12-14 at 13 56
38](https://user-images.githubusercontent.com/685609/207723593-40535089-1436-4b62-81cc-d098d589f83a.png)
- Added `ReceiptParser` scheme to allow building the target from Xcode:
![Screenshot 2022-12-14 at 13 57
02](https://user-images.githubusercontent.com/685609/207723665-dace0aa1-4ae7-4382-895f-908f356043b4.png)
- Added CI `spm-receipt-parser` job
- Added CI `installation-tests-receipt-parser` job
- Added `ReceiptParserAPITester` to test API for `ReceiptParser`'s
target
- Compile `ReceiptParserAPITester` when running `RevenueCat` tests so
these are validated as well. -

[CSDK-17]:
https://revenuecats.atlassian.net/browse/CSDK-17?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
2022-12-22 12:02:00 -08:00
NachoSoto 3b7c93c2b7 ReceiptParser: a few documentation improvements (#2189) 2022-12-22 11:24:27 -08:00
NachoSoto b3875a9380 Exposed PurchasesReceiptParser and AppleReceipt (#2153)
Initial step for [CSDK-17]. Fixes [CSDK-523].

### Changes:
- Renamed `ReceiptParser` to `PurchasesReceiptParser` to avoid name
collision with future SPM `ReceiptParser`
- Exposed `ReceiptParser`, `AppleReceipt`,
`PurchasesReceiptParser.Error`
- Added new types to `SwiftAPITester`
- Added docs to these types
- Moved `LoggerType` to be available to `ReceiptParser`
- Moved `DateFormatter+Extensions` to be available to `ReceiptParser`
- Moved `Codable+Extensions` to be available to `ReceiptParser`
- Created `ReceiptParserLogger` for `ReceiptParser`


[CSDK-17]:
https://revenuecats.atlassian.net/browse/CSDK-17?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ

[CSDK-523]:
https://revenuecats.atlassian.net/browse/CSDK-523?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
2022-12-22 10:09:12 -08:00
NachoSoto 8d6f80b362 Added a few performance improvements for ReceiptParser (#2124) 2022-12-13 11:08:21 -08:00
NachoSoto 903d3b3958 ReceiptParser: ensure parsing never happens in the main thread (#2123)
Fixes #2107 and [SDKONCALL-180].

[This
comment](https://github.com/RevenueCat/purchases-ios/issues/2107#issuecomment-1342846583)
pointed out that parsing a long receipt can freeze the UI for almost a
second.

⚠️ This new assertion (running only in integration tests using the code
added in #2100) has exposed a couple of code paths where we parse
receipts from the main thread!

[SDKONCALL-180]:
https://revenuecats.atlassian.net/browse/SDKONCALL-180?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
2022-12-13 08:24:12 -08:00
NachoSoto 2cca7deadb ReceiptParser.parse always throws ReceiptParser.Error (#2099)
This will be used for [CSDK-17]. 

Since this will be a public API, we want to make sure we provide
consistent and documented errors.
This changes a few thrown `BitShiftError`s into `ReceiptParser.Error`.

[CSDK-17]:
https://revenuecats.atlassian.net/browse/CSDK-17?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
2022-12-08 17:17:57 -08:00
NachoSoto 8b4a9ecbca PostReceiptDataOperation: log Apple error when purchase equals expiration date (#2038)
A large amount of flaky integration tests are due to this bug, so
detecting it early in the logs will help debug this.
Radar: `FB11767559`.

`productType` is missing in `StoreKitTest` purchases, so I changed
`isSubscription` to default to checking if there is an expiration when
the type is `.unknown`.
To simplify the code, I changed `ProductType?` to be non-optional, since
we already have `.unknown` as a case.

### Example:
```
[00:27:56]: ▸ 2022-11-09 00:27:56.000533+0000 BackendIntegrationTestsHostApp[3942:17000] [Purchases] - DEBUG: ℹ️ PostReceiptDataOperation: Posting receipt: {
[00:27:56]: ▸   "opaque_value" : "1n+XXw8AAAA=",
[00:27:56]: ▸   "sha1_hash" : "unR7vAzPaUlB0TtZ09z5jMyqqAs=",
[00:27:56]: ▸   "bundle_id" : "com.revenuecat.StoreKitTestApp",
[00:27:56]: ▸   "in_app_purchases" : [
[00:27:56]: ▸     {
[00:27:56]: ▸       "quantity" : 1,
[00:27:56]: ▸       "product_id" : "com.revenuecat.weekly_1.99.3_day_intro",
[00:27:56]: ▸       "purchase_date" : "2022-11-09T00:27:50Z",
[00:27:56]: ▸       "transaction_id" : "0",
[00:27:56]: ▸       "is_in_intro_offer_period" : true,
[00:27:56]: ▸       "expires_date" : "2022-11-09T00:27:50Z"
[00:27:56]: ▸     },
[00:27:56]: ▸     {
[00:27:56]: ▸       "product_id" : "com.revenuecat.weekly_1.99.3_day_intro",
[00:27:56]: ▸       "quantity" : 1,
[00:27:56]: ▸       "transaction_id" : "1",
[00:27:56]: ▸       "is_in_intro_offer_period" : false,
[00:27:56]: ▸       "expires_date" : "2022-11-09T00:28:00Z",
[00:27:56]: ▸       "original_purchase_date" : "2022-11-09T00:27:50Z",
[00:27:56]: ▸       "original_transaction_id" : "0",
[00:27:56]: ▸       "purchase_date" : "2022-11-09T00:27:50Z"
[00:27:56]: ▸     }
[00:27:56]: ▸   ],
[00:27:56]: ▸   "application_version" : "1",
[00:27:56]: ▸   "creation_date" : "2022-11-09T00:27:55Z",
[00:27:56]: ▸   "expiration_date" : "4001-01-01T00:00:00Z"
[00:27:56]: ▸ }
[00:27:56]: ▸ 2022-11-09 00:27:56.000710+0000 BackendIntegrationTestsHostApp[3942:17000] [Purchases] - ERROR: 🍎‼️ Receipt for product 'com.revenuecat.weekly_1.99.3_day_intro' has the same purchase (2022-11-09 00:27:50 +0000) and expiration (2022-11-09 00:27:50 +0000) dates. This is likely a StoreKit bug.
```
2022-11-09 11:18:52 -08:00
NachoSotoandCesar de la Vega c00be57a13 ReceiptFetcher: added retry mechanism (#1945)
Fixes [CSDK-478]

~~Depends on #1941, #1942, and #1943.~~

### Example:

Log of a successful purchase after retrying 🎉 

> INFO: 💰 Purchasing Product 'com.revenuecat.monthly_4.99.1_week_intro'
DEBUG: ℹ️ Loaded receipt from url
file:///Users/nachosoto/Library/Developer/CoreSimulator/Devices/A3576DC2-355E-45BA-B32C-D2C0A3811BB4/data/Containers/Data/Application/7ACD12DA-2A12-4CF1-8A76-8295E40B64EF/StoreKit/receipt
INFO: ℹ️ Receipt parsed successfully
WARN: 🍎‼️ Local receipt is still missing purchase for
'com.revenuecat.monthly_4.99.1_week_intro':
{
  "opaque_value" : "79v2XwwAAAA=",
  "sha1_hash" : "GhOmfuioo2Z4R3zX8K60l4b0qgs=",
  "bundle_id" : "com.revenuecat.StoreKitTestApp",
  "in_app_purchases" : [
    {
      "quantity" : 1,
      "product_id" : "com.revenuecat.monthly_4.99.1_week_intro",
      "purchase_date" : "2022-09-28T01:14:24Z",
      "transaction_id" : "0",
      "is_in_intro_offer_period" : true,
      "expires_date" : "2022-09-28T01:14:34Z"
    }
  ],
  "application_version" : "1",
  "creation_date" : "2022-09-28T01:14:24Z",
  "expiration_date" : "4001-01-01T00:00:00Z"
}
DEBUG: ℹ️ Retrying Receipt fetch after  5 seconds
DEBUG: ℹ️ API request completed: POST /v1/receipts 200
DEBUG: ℹ️ PostReceiptDataOperation: Finished
DEBUG: ℹ️ Serial request done: POST receipts, 0 requests left in the
queue
DEBUG: ℹ️ Sending updated CustomerInfo to delegate.
DEBUG: ℹ️ Found 0 unsynced attributes for App User ID:
$RCAnonymousID:ec326c1613c14b489980eace90dceabb
DEBUG: ℹ️ Force refreshing the receipt to get latest transactions from
Apple.
DEBUG: ℹ️ Loaded receipt from url
file:///Users/nachosoto/Library/Developer/CoreSimulator/Devices/A3576DC2-355E-45BA-B32C-D2C0A3811BB4/data/Containers/Data/Application/7ACD12DA-2A12-4CF1-8A76-8295E40B64EF/StoreKit/receipt
DEBUG: ℹ️ PostReceiptDataOperation: Started
INFO: ℹ️ Receipt parsed successfully
DEBUG: ℹ️ PostReceiptDataOperation: Posting receipt: {
  "opaque_value" : "0nf1uwEAAAA=",
  "sha1_hash" : "lfxVVsBuSkQLtrGJseZ8zlpXc5A=",
  "bundle_id" : "com.revenuecat.StoreKitTestApp",
  "in_app_purchases" : [
    {
      "quantity" : 1,
      "product_id" : "com.revenuecat.monthly_4.99.1_week_intro",
      "purchase_date" : "2022-09-28T01:14:24Z",
      "transaction_id" : "0",
      "is_in_intro_offer_period" : true,
      "expires_date" : "2022-09-28T01:14:34Z"
    },
    {
      "product_id" : "com.revenuecat.monthly_4.99.1_week_intro",
      "quantity" : 1,
      "transaction_id" : "1",
      "is_in_intro_offer_period" : false,
      "expires_date" : "2022-09-28T01:15:04Z",
      "original_purchase_date" : "2022-09-28T01:14:24Z",
      "original_transaction_id" : "0",
      "purchase_date" : "2022-09-28T01:14:34Z"
    }
  ],
  "application_version" : "1",
  "creation_date" : "2022-09-28T01:14:35Z",
  "expiration_date" : "4001-01-01T00:00:00Z"
}
DEBUG: ℹ️ There are no requests currently running, starting request POST
receipts
DEBUG: ℹ️ API request started: POST /v1/receipts
DEBUG: ℹ️ API request completed: POST /v1/receipts 200
DEBUG: ℹ️ PostReceiptDataOperation: Finished
DEBUG: ℹ️ Serial request done: POST receipts, 0 requests left in the
queue
DEBUG: ℹ️ Sending updated CustomerInfo to delegate.
DEBUG: ℹ️ Loaded receipt from url
file:///Users/nachosoto/Library/Developer/CoreSimulator/Devices/A3576DC2-355E-45BA-B32C-D2C0A3811BB4/data/Containers/Data/Application/7ACD12DA-2A12-4CF1-8A76-8295E40B64EF/StoreKit/receipt
INFO: ℹ️ Receipt parsed successfully
DEBUG: ℹ️ Skipping products request because products were already
cached. products: ["com.revenuecat.monthly_4.99.1_week_intro"]
DEBUG: ℹ️ Skipping products request because products were already
cached. products: ["com.revenuecat.monthly_4.99.1_week_intro"]
DEBUG: ℹ️ Store products request finished
DEBUG: ℹ️ Found 0 unsynced attributes for App User ID:
$RCAnonymousID:ec326c1613c14b489980eace90dceabb
DEBUG: ℹ️ PostReceiptDataOperation: Started
INFO: ℹ️ Receipt parsed successfully
DEBUG: ℹ️ PostReceiptDataOperation: Posting receipt: {
  "opaque_value" : "tv4\/2gcAAAA=",
  "sha1_hash" : "0rh1naTc3JyfM16M\/1f6fw4ezAY=",
  "bundle_id" : "com.revenuecat.StoreKitTestApp",
  "in_app_purchases" : [
    {
      "quantity" : 1,
      "product_id" : "com.revenuecat.monthly_4.99.1_week_intro",
      "purchase_date" : "2022-09-28T01:14:24Z",
      "transaction_id" : "0",
      "is_in_intro_offer_period" : true,
      "expires_date" : "2022-09-28T01:14:34Z"
    },
    {
      "product_id" : "com.revenuecat.monthly_4.99.1_week_intro",
      "quantity" : 1,
      "transaction_id" : "1",
      "is_in_intro_offer_period" : false,
      "expires_date" : "2022-09-28T01:15:04Z",
      "original_purchase_date" : "2022-09-28T01:14:24Z",
      "original_transaction_id" : "0",
      "purchase_date" : "2022-09-28T01:14:34Z"
    }
  ],
  "application_version" : "1",
  "creation_date" : "2022-09-28T01:14:40Z",
  "expiration_date" : "4001-01-01T00:00:00Z"
}
DEBUG: ℹ️ There are no requests currently running, starting request POST
receipts
DEBUG: ℹ️ API request started: POST /v1/receipts
DEBUG: ℹ️ API request completed: POST /v1/receipts 200
DEBUG: ℹ️ PostReceiptDataOperation: Finished
DEBUG: ℹ️ Serial request done: POST receipts, 0 requests left in the
queue
INFO: 😻💰 Purchased product - 'com.revenuecat.monthly_4.99.1_week_intro'

[CSDK-478]:
https://revenuecats.atlassian.net/browse/CSDK-478?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ

Co-authored-by: Cesar de la Vega <cesarvegaro@gmail.com>
2022-10-07 07:55:53 -07:00
NachoSoto 9ad9e9b108 Moved InAppPurchase into AppleReceipt (#1942)
The name `InAppPurchase` is too general. This type in particular is
specific to the content of `AppleReceipt`, so nesting it makes that
clear.
Follow up to #1933.
2022-09-28 08:40:17 -07:00
NachoSoto b32859d0c8 PostReceiptDataOperation: print receipt data if debug logs are enabled (#1940)
For [CSDK-478].
Follow up to #1929.

### Example:
> DEBUG: ℹ️ PostReceiptDataOperation: Started
INFO: ℹ️ Receipt parsed successfully
DEBUG: ℹ️ PostReceiptDataOperation: Posting receipt: {
  "opaque_value" : "Rn\/39QYAAAA=",
  "sha1_hash" : "Ii8Z7ocZm524eJcWFOvVauNrNHs=",
  "bundle_id" : "com.revenuecat.StoreKitTestApp",
  "in_app_purchases" : [
    {
      "quantity" : 1,
      "product_id" : "com.revenuecat.monthly_4.99.1_week_intro",
      "purchase_date" : "2022-09-27T19:09:47Z",
      "transaction_id" : "0",
      "is_in_intro_offer_period" : true,
      "expires_date" : "2022-09-27T19:09:57Z"
    }
  ],
  "application_version" : "1",
  "creation_date" : "2022-09-27T19:09:47Z",
  "expiration_date" : "4001-01-01T00:00:00Z"
}
DEBUG: ℹ️ There are no requests currently running, starting request POST
receipts
DEBUG: ℹ️ API request started: POST /v1/receipts
DEBUG: ℹ️ API request completed: POST /v1/receipts 200
DEBUG: ℹ️ PostReceiptDataOperation: Finished
DEBUG: ℹ️ Serial request done: POST receipts, 0 requests left in the
queue
DEBUG: ℹ️ Sending updated CustomerInfo to delegate.
INFO: 😻💰 Purchased product - 'com.revenuecat.monthly_4.99.1_week_intro'
INFO: 💰 Finishing transaction com.revenuecat.monthly_4.99.1_week_intro 0
()

[CSDK-478]:
https://revenuecats.atlassian.net/browse/CSDK-478?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
2022-09-27 12:50:34 -07:00
NachoSoto adf6353c83 AppleReceipt: refactored declarations into nested types (#1933)
Before this change we had a lot of global types. Now they're all nested
inside of `AppleReceipt`, `InAppPurchase`, and `InAppPurchaseBuilder`.
2022-09-23 10:38:14 -07:00
NachoSoto c4cfdb19e2 StoreKitIntegrationTests: print AppleReceipt data whenever verifyEntitlementWentThrough fails (#1929)
This will help us debug flaky tests.

Example:
> INFO: 😻💰 Purchased product -
'com.revenuecat.monthly_4.99.1_week_intro'
DEBUG: ℹ️ Force refreshing the receipt to get latest transactions from
Apple.
DEBUG: ℹ️ Loaded receipt from url
file:///Users/nachosoto/Library/Developer/CoreSimulator/Devices/A3576DC2-355E-45BA-B32C-D2C0A3811BB4/data/Containers/Data/Application/EDF3B7C8-A4BD-4629-813E-0934528AF02E/StoreKit/receipt
INFO: ℹ️ Receipt parsed successfully
INFO: ℹ️ Receipt content:
["inAppPurchases": [], "creationDate": 2022-09-21 21:00:07 +0000,
"opaqueValue": 8 bytes, "applicationVersion": "1",
"originalApplicationVersion": "<unknown>", "bundleId":
"com.revenuecat.StoreKitTestApp", "expirationDate": 4001-01-01 00:00:00
+0000, "sha1Hash": 20 bytes]

/Users/nachosoto/dev/purchases-ios/Tests/BackendIntegrationTests/StoreKitIntegrationTests.swift:64:
error: -[BackendIntegrationTests.StoreKit2IntegrationTests
testCanPurchaseProduct] : failed - Expected Entitlement. Got: [:]
expected to have Dictionary<String, EntitlementInfo> with count 1, got 0
Actual Value: [:]

I've also added the receipt as a test attachment so we can download it
from `CircleCI`:

![Screenshot 2022-09-21 at 14 33
01](https://user-images.githubusercontent.com/685609/191614494-c1b1b24e-133d-45f2-83b5-f4cd1f491294.png)
2022-09-21 14:56:14 -07:00
NachoSoto aae60d0f51 ReceiptParser: Sendable conformance (#1825)
For [CSDK-379].
Extracted from #1795 to make it easier to review this in isolation.

Also made `ASN1ObjectIdentifierBuilder` an `enum` since we don't need to create instances of it to use it.

[CSDK-379]: https://revenuecats.atlassian.net/browse/CSDK-379?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
2022-08-22 16:01:19 -07:00
aboedo 4d6c7bd5b8 More tests for receipt parsing with storekit test (#1456)
* made a few attributes that aren't present in SKTest receipts optional, added test with IAP

* added more tests for every field in the receipt

* fixed a few places where `asDict` wasn't correctly considering nullability in the IAP receipt fields
2022-04-06 15:44:00 -03:00
aboedo fb193e37c8 Fix: ASN1 length calculation for indefinite-length (#1452)
* fixed length calculation for indefinite-length containers

* temporarily removed the early return from empty receipts

* fixed length calculation when parsing receipts with indefinite length

* fix linting
2022-04-06 11:02:59 -03:00
aboedo cab0053885 Fix: local receipt parsing for StoreKitTest - part 1 (#1446)
* added basic ugly test

* fixed local receipt parsing for sktest, broke regular receipt parsing temporarily

* updated length calculation so that it works for both storekit test and regular receipts

* updated test to allow for missing OriginalAppVersion in receipt

* fixed linting in imports

* fixed checks for non-storekitTest receipts

* added a longer comment to explain the length == 0 case

* cleaned up the still-ugly tests for the receipt parser
2022-04-05 16:18:06 -03:00
aboedo f5697de730 renamed Purchases folder to Sources (#1416)
* renamed `Purchases` folder to `Sources`

* temporarily enabling installation test jobs

* re-disabling installation test jobs
2022-03-28 11:53:24 -03:00