Skip to content

Commit 0210e00

Browse files
committed
Prepare for NuGet package
1 parent 6f190f5 commit 0210e00

17 files changed

+121
-133
lines changed

.github/workflows/build.yml

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,24 +5,27 @@ name: build
55

66
on:
77
push:
8-
branches: [ "main" ]
8+
branches: ["main"]
99
pull_request:
10-
branches: [ "main" ]
10+
branches: ["main"]
1111

1212
jobs:
1313
build:
14-
1514
runs-on: ubuntu-latest
1615

1716
steps:
18-
- uses: actions/checkout@v4
19-
- name: Setup .NET
20-
uses: actions/setup-dotnet@v4
21-
with:
22-
dotnet-version: 8.0.x
23-
- name: Restore dependencies
24-
run: dotnet restore
25-
- name: Build
26-
run: dotnet build --no-restore
27-
- name: Test
28-
run: dotnet test --no-build --verbosity normal
17+
- uses: actions/checkout@v4
18+
19+
- name: Setup .NET
20+
uses: actions/setup-dotnet@v4
21+
with:
22+
dotnet-version: 8.0.x
23+
24+
- name: Restore dependencies
25+
run: dotnet restore
26+
27+
- name: Build
28+
run: dotnet build --no-restore
29+
30+
- name: Test
31+
run: dotnet test --no-build --verbosity normal

.github/workflows/release.yml

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
name: Release to NuGet
2+
3+
on:
4+
release:
5+
types: [published]
6+
7+
jobs:
8+
build:
9+
runs-on: ubuntu-latest
10+
11+
steps:
12+
- uses: actions/checkout@v4
13+
14+
- name: Setup .NET
15+
uses: actions/setup-dotnet@v4
16+
with:
17+
dotnet-version: 8.0.x
18+
19+
- name: Test
20+
run: dotnet test --verbosity normal
21+
22+
- name: Pack
23+
run: dotnet pack src/AppStoreServerLibrary.csproj --configuration Release
24+
25+
- name: Push to NuGet
26+
run: dotnet nuget push src/bin/Release/*.nupkg --api-key ${{ secrets.NUGET_API_KEY }} --source https://api.nuget.org/v3/index.json
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11

22
Microsoft Visual Studio Solution File, Format Version 12.00
3-
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AppStoreServerLibraryDotnet", "src\AppStoreServerLibraryDotnet.csproj", "{07591682-FEF0-4441-A504-C479651AA37A}"
3+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AppStoreServerLibrary", "src\AppStoreServerLibrary.csproj", "{07591682-FEF0-4441-A504-C479651AA37A}"
44
EndProject
5-
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AppStoreServerLibraryDotnetTests", "tests\AppStoreServerLibraryDotnetTests.csproj", "{3640D40C-C3F9-46C3-B39F-C903BDEDEB80}"
5+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AppStoreServerLibraryTests", "tests\AppStoreServerLibraryTests.csproj", "{3640D40C-C3F9-46C3-B39F-C903BDEDEB80}"
66
EndProject
77
Global
88
GlobalSection(SolutionConfigurationPlatforms) = preSolution

README.md

Lines changed: 42 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,8 @@
1-
# app-store-server-library-dotnet
1+
# App Store Server Library for .NET
22

3-
An unofficial .NET SDK for App Store Server Notifications and API
3+
An unofficial .NET SDK for App Store Server Notifications v2 and API
44

5-
This library started as helpers to work with the App Store Server Notifications. Using it you can verify and decode notifications, transactions and renewal infos. It implements the required security protocols to verify the payload.
6-
7-
It also provides a way to interact with the App Store API and to decode the Store kit 1 receipt data to retrieve the Transaction Id.
8-
9-
The library design is copied from the official Apple libraries and it is intended to be used in the same way.
5+
The overall library and API design is copied from the official Apple libraries and it is intended to be used in the same way.
106
More resources:
117

128
- [Meet the App Store Server Library](https://developer.apple.com/videos/play/wwdc2023/10143/)
@@ -34,20 +30,16 @@ This is a list of the features that are currently implemented in the library, co
3430
| Extending the renewal date for auto-renewable subscriptions || |
3531
| Request a Test Notification || |
3632

37-
To summarize, it's missing a full coverage on the API endpoints.
38-
3933
## Installation
4034

4135
The library is available on NuGet. You can install it using the following command:
4236

4337
```bash
44-
TBD
38+
dotnet add package Mimo.AppStoreServerLibrary
4539
```
4640

4741
## Usage
4842

49-
The library is designed to be used in the same way as the official Apple libraries.
50-
5143
### Obtaining Apple Root Certificates
5244

5345
Download and store the root certificates found in the Apple Root Certificates section of the Apple PKI site. Provide these certificates as an array to a SignedDataVerifier to allow verifying the signed data comes from Apple.
@@ -57,29 +49,22 @@ Download and store the root certificates found in the Apple Root Certificates se
5749
Here is an example of how to verify a notification:
5850

5951
```csharp
60-
//This is the endpoint you could use to receive the notifications from Apple
61-
app.MapPost("/verify-decode-notification", async (ISignedDataVerifier signedDataVerifier, [FromBody] ResponseBodyV2 request) =>
62-
{
63-
//First decode the notification
64-
var decodedNotificaiton = await signedDataVerifier.VerifyAndDecodeNotification(request.SignedPayload);
65-
66-
//Then you can decode the transaction
67-
JwsTransactionDecodedPayload decodedTransaction =
68-
await signedDataVerifier.VerifyAndDecodeTransaction(decodedNotificaiton.Data.SignedTransactionInfo!);
69-
70-
//And the renewal info
71-
JWSRenewalInfoDecodedPayload decodedRenewalInfo =
72-
await signedDataVerifier.VerifyAndDecodeRenewalInfo(decodedNotificaiton.Data.SignedRenewalInfo!);
52+
var signedDataVerifier = new SignedDataVerifier(
53+
appleRootCertificates: rootCertificatesBytes,
54+
enableOnlineChecks: true,
55+
environment: AppStoreEnvironment.Sandbox,
56+
bundleId: "com.example.app"
57+
);
58+
59+
try {
60+
var decodedPayload = await signedDataVerifier.VerifyAndDecodeNotification(signedPayload);
61+
62+
} catch (VerificationException ex) {
63+
Console.WriteLine($"Verification failed: {ex.Message}");
64+
}
7365

74-
return decodedNotificaiton;
75-
})
76-
.WithName("VerifyAndDecodeNotification")
77-
.WithOpenApi();
7866
```
7967

80-
If the verification fails it will raise a `VerificationException` exception with failure details.
81-
Once you get the decoded transaction, you can use `VerifyAndDecodeTransaction` and `VerifyAndDecodeRenewalInfo` to decode the transaction and renewal info.
82-
8368
> [!IMPORTANT]
8469
> The `SignedDataVerifier` will not verify the payload if the `environment` parameter is `LocalTesting`. This was done to allow testing fake payloads locally without the need to verify them.
8570
@@ -88,66 +73,41 @@ Once you get the decoded transaction, you can use `VerifyAndDecodeTransaction` a
8873
Here is an example of how to get all notification history for a specific transaction using the `Pagination token` :
8974

9075
```csharp
91-
GetAppStoreNotificationHistoryResponse results = new([]);
92-
93-
NotificationHistoryRequest appStoreRequest = new()
76+
// Create a request for notification history
77+
NotificationHistoryRequest request = new()
9478
{
95-
//At which date the history should start
96-
StartDate = request.StartDate.ToUnixTimeMilliseconds(),
97-
//At which date the history should end
98-
EndDate = request.EndDate.ToUnixTimeMilliseconds(),
99-
//The transaction id to get the history for, if not set it will get all the history
100-
TransactionId = request.TransactionId
79+
StartDate = DateTimeOffset.UtcNow.AddDays(-7).ToUnixTimeMilliseconds(),
80+
EndDate = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(),
81+
// Optional: filter by transaction ID
82+
TransactionId = "your-transaction-id"
10183
};
10284

85+
// Get initial page of notifications
10386
NotificationHistoryResponse? notifications =
104-
await appStoreServerClient.GetNotificationHistory(appStoreRequest);
87+
await appStoreServerClient.GetNotificationHistory(request);
10588

10689
if (notifications != null)
10790
{
108-
//For clarity of this example, ExtractNotificationInfo is a helper method that will
109-
//call ISignedDataVerifier.VerifyAndDecodeNotification for each notification and
110-
//extract any required info
111-
results.Transactions.AddRange(await this.ExtractNotificationInfo(notifications));
112-
113-
//While there are more notifications to get, get them
114-
while (notifications!.HasMore)
91+
// Process notifications on current page
92+
foreach (var notification in notifications.NotificationHistory)
11593
{
116-
notifications = await appStoreHelper.GetNotificationHistory(appStoreRequest,
117-
notifications.PaginationToken);
94+
Console.WriteLine($"Notification ID: {notification.SignedPayload}");
95+
}
11896

119-
results.Transactions.AddRange(await this.ExtractNotificationInfo(notifications!));
97+
// Get additional pages if they exist
98+
while (notifications.HasMore)
99+
{
100+
notifications = await appStoreServerClient.GetNotificationHistory(
101+
request,
102+
notifications.PaginationToken
103+
);
104+
105+
foreach (var notification in notifications!.NotificationHistory)
106+
{
107+
Console.WriteLine($"Notification ID: {notification.SignedPayload}");
108+
}
120109
}
121110
}
122-
123-
//Return the list of retrieved and decoded notifications
124-
return results;
125-
```
126-
127-
And another example on how to get the subscription status for a specific subscription using the `Transaction Id` :
128-
129-
```csharp
130-
string transactionId = "your-transaction-id";
131-
132-
//Inject IAppStoreServerAPIClient as appStoreServerClient to use the API
133-
SubscriptionStatusResponse response = await appStoreServerClient.GetAllSubscriptionStatuses(transactionId);
134-
135-
//As we retrieved the subscription status based on the original transaction id, the response already contains only
136-
//the TransactionItem related to the subscription we are looking for
137-
//Also the LastTransactions property, if we believe the documentation, only contains the most recent transaction.
138-
//It means that even if it's a list it should contain only one item.
139-
SubscriptionStatusLastTransactionsItem lastRemoteTransaction = response.Data
140-
.Last()
141-
.LastTransactions.Last();
142-
143-
//And inject ISignedDataVerifier as signedDataVerifier to decode the transaction and renewal info
144-
JwsTransactionDecodedPayload decodedTransaction =
145-
await signedDataVerifier.VerifyAndDecodeTransaction(lastRemoteTransaction.SignedTransactionInfo!);
146-
147-
JWSRenewalInfoDecodedPayload decodedRenewalInfo =
148-
await signedDataVerifier.VerifyAndDecodeRenewalInfo(lastRemoteTransaction.SignedRenewalInfo!);
149-
150-
Console.WriteLine($"The subscription status is {lastRemoteTransaction.Status} for transaction Id {decodedTransaction.TransactionId}");
151111
```
152112

153113
### Receipt Utility
@@ -156,9 +116,7 @@ Here is an example of how to extract the transaction id from a receipt:
156116

157117
```csharp
158118
string receiptData = "your-receipt-data";
159-
ReceiptUtility utility = new ReceiptUtility();
119+
ReceiptUtility utility = new();
160120

161121
string transactionId = utility.ExtractTransactionIdFromAppReceipt(receiptData);
162122
```
163-
164-
Note that the Receipt Utility is not injectable and should be instantiated when needed.

src/AppStoreServerApiClient.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@
44
using System.Text;
55
using System.Text.Json;
66
using System.Web;
7-
using Mimo.AppStoreServerLibraryDotnet.Exceptions;
8-
using Mimo.AppStoreServerLibraryDotnet.Models;
7+
using Mimo.AppStoreServerLibrary.Exceptions;
8+
using Mimo.AppStoreServerLibrary.Models;
99
using Microsoft.IdentityModel.JsonWebTokens;
1010
using Microsoft.IdentityModel.Tokens;
1111

12-
namespace Mimo.AppStoreServerLibraryDotnet;
12+
namespace Mimo.AppStoreServerLibrary;
1313

1414
/// <summary>
1515
/// Create an App Store Server API client
Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,10 @@
55
<ImplicitUsings>enable</ImplicitUsings>
66
<Nullable>enable</Nullable>
77
<Company>Mimo</Company>
8-
<PackageId>Mimo.AppStoreServerLibraryDotnet</PackageId>
8+
<PackageId>Mimo.AppStoreServerLibrary</PackageId>
9+
<Version>0.0.1</Version>
910
<Authors>Mimo</Authors>
10-
<Title>Mimo.AppStoreServerLibraryDotnet</Title>
11+
<Title>Mimo.AppStoreServerLibrary</Title>
1112
<Description>An unofficial .NET SDK for App Store Server Notifications and API</Description>
1213
<Copyright>Copyright © Mimo GmbH 2024</Copyright>
1314
<PackageLicenseExpression>MIT</PackageLicenseExpression>
@@ -16,8 +17,8 @@
1617
<PackageTags>Apple, StoreKit 2, App Store Server Notifications, App Store Server API, App Store Server Library, AppStore</PackageTags>
1718
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
1819
<PackageReadmeFile>README.md</PackageReadmeFile>
19-
<AssemblyName>Mimo.AppStoreServerLibraryDotnet</AssemblyName>
20-
<RootNamespace>Mimo.AppStoreServerLibraryDotnet</RootNamespace>
20+
<AssemblyName>Mimo.AppStoreServerLibrary</AssemblyName>
21+
<RootNamespace>Mimo.AppStoreServerLibrary</RootNamespace>
2122
</PropertyGroup>
2223

2324
<ItemGroup>

src/Exceptions/ApiException.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
using System.Net;
2-
using Mimo.AppStoreServerLibraryDotnet.Models;
2+
using Mimo.AppStoreServerLibrary.Models;
33

4-
namespace Mimo.AppStoreServerLibraryDotnet.Exceptions;
4+
namespace Mimo.AppStoreServerLibrary.Exceptions;
55

66
public class ApiException(HttpStatusCode? httpStatusCode, ErrorResponse? errorResponse = null, Exception? innerException = null) : Exception(errorResponse?.ErrorMessage, innerException)
77
{

src/Exceptions/VerificationException.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11

2-
namespace Mimo.AppStoreServerLibraryDotnet.Exceptions;
2+
namespace Mimo.AppStoreServerLibrary.Exceptions;
33

44
public class VerificationException(string message) : Exception(message)
55
{

src/Models/AppStoreDataTypes.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
namespace Mimo.AppStoreServerLibraryDotnet.Models;
1+
namespace Mimo.AppStoreServerLibrary.Models;
22

33
/// <summary>
44
/// The response body the App Store sends in a version 2 server notification.

src/Models/AppStoreEnvironment.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
namespace Mimo.AppStoreServerLibraryDotnet.Models;
1+
namespace Mimo.AppStoreServerLibrary.Models;
22

33
public record AppStoreEnvironment
44
{

0 commit comments

Comments
 (0)