Skip to content

Commit 03859dc

Browse files
Merge pull request #103 from alexanderjordanbaker/AppStoreServerAPI112
Add support for App Store Server API v1.12 and App Store Server Notif…
2 parents 22f2a83 + 47d8340 commit 03859dc

File tree

7 files changed

+164
-8
lines changed

7 files changed

+164
-8
lines changed

src/main/java/com/apple/itunes/storekit/client/AppStoreServerAPIClient.java

+11-2
Original file line numberDiff line numberDiff line change
@@ -264,17 +264,26 @@ public NotificationHistoryResponse getNotificationHistory(String paginationToken
264264
return makeHttpCall("/inApps/v1/notifications/history", "POST", queryParameters, notificationHistoryRequest, NotificationHistoryResponse.class);
265265
}
266266

267+
/**
268+
* @see #getTransactionHistory(String, String, TransactionHistoryRequest, GetTransactionHistoryVersion)
269+
*/
270+
@Deprecated(since = "2.2.0")
271+
public HistoryResponse getTransactionHistory(String transactionId, String revision, TransactionHistoryRequest transactionHistoryRequest) throws APIException, IOException {
272+
return this.getTransactionHistory(transactionId, revision, transactionHistoryRequest, GetTransactionHistoryVersion.V1);
273+
}
274+
267275
/**
268276
* Get a customer’s in-app purchase transaction history for your app.
269277
*
270278
* @param transactionId The identifier of a transaction that belongs to the customer, and which may be an original transaction identifier.
271279
* @param revision A token you provide to get the next set of up to 20 transactions. All responses include a revision token. Note: For requests that use the revision token, include the same query parameters from the initial request. Use the revision token from the previous HistoryResponse.
280+
* @param version The version of the Get Transaction History endpoint to use. V2 is recommended.
272281
* @return A response that contains the customer’s transaction history for an app.
273282
* @throws APIException If a response was returned indicating the request could not be processed
274283
* @throws IOException If an exception was thrown while making the request
275284
* @see <a href="https://developer.apple.com/documentation/appstoreserverapi/get_transaction_history">Get Transaction History</a>
276285
*/
277-
public HistoryResponse getTransactionHistory(String transactionId, String revision, TransactionHistoryRequest transactionHistoryRequest) throws APIException, IOException {
286+
public HistoryResponse getTransactionHistory(String transactionId, String revision, TransactionHistoryRequest transactionHistoryRequest, GetTransactionHistoryVersion version) throws APIException, IOException {
278287
Map<String, List<String>> queryParameters = new HashMap<>();
279288
if (revision != null) {
280289
queryParameters.put("revision", List.of(revision));
@@ -303,7 +312,7 @@ public HistoryResponse getTransactionHistory(String transactionId, String revisi
303312
if (transactionHistoryRequest.getRevoked() != null) {
304313
queryParameters.put("revoked", List.of(transactionHistoryRequest.getRevoked().toString()));
305314
}
306-
return makeHttpCall("/inApps/v1/history/" + transactionId, "GET", queryParameters, null, HistoryResponse.class);
315+
return makeHttpCall("/inApps/" + version.getUrlVersion() + "/history/" + transactionId, "GET", queryParameters, null, HistoryResponse.class);
307316
}
308317

309318
/**
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package com.apple.itunes.storekit.client;
2+
3+
public enum GetTransactionHistoryVersion {
4+
5+
@Deprecated(since = "2.2.0")
6+
V1("v1"),
7+
V2("v2");
8+
9+
private final String urlVersion;
10+
11+
GetTransactionHistoryVersion(String urlVersion) {
12+
this.urlVersion = urlVersion;
13+
}
14+
15+
String getUrlVersion() {
16+
return urlVersion;
17+
}
18+
}

src/main/java/com/apple/itunes/storekit/model/JWSRenewalInfoDecodedPayload.java

+83-1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ public class JWSRenewalInfoDecodedPayload implements DecodedSignedData {
2929
private static final String SERIALIZED_NAME_ENVIRONMENT = "environment";
3030
private static final String SERIALIZED_NAME_RECENT_SUBSCRIPTION_START_DATE = "recentSubscriptionStartDate";
3131
private static final String SERIALIZED_NAME_RENEWAL_DATE = "renewalDate";
32+
private static final String SERIALIZED_NAME_RENEWAL_PRICE = "renewalPrice";
33+
private static final String SERIALIZED_NAME_CURRENCY = "currency";
34+
private static final String SERIALIZED_NAME_OFFER_DISCOUNT_TYPE = "offerDiscountType";
3235
@JsonProperty(SERIALIZED_NAME_EXPIRATION_INTENT)
3336
private Integer expirationIntent;
3437
@JsonProperty(SERIALIZED_NAME_ORIGINAL_TRANSACTION_ID)
@@ -61,6 +64,12 @@ public class JWSRenewalInfoDecodedPayload implements DecodedSignedData {
6164
@JsonProperty(SERIALIZED_NAME_RENEWAL_DATE)
6265
@JsonDeserialize(using=XcodeCompatibleTimestampDeserializer.class)
6366
private Long renewalDate;
67+
@JsonProperty(SERIALIZED_NAME_RENEWAL_PRICE)
68+
private Long renewalPrice;
69+
@JsonProperty(SERIALIZED_NAME_CURRENCY)
70+
private String currency;
71+
@JsonProperty(SERIALIZED_NAME_OFFER_DISCOUNT_TYPE)
72+
private String offerDiscountType;
6473
@JsonAnySetter
6574
private Map<String, Object> unknownFields;
6675

@@ -389,6 +398,73 @@ public void setRenewalDate(Long renewalDate) {
389398
this.renewalDate = renewalDate;
390399
}
391400

401+
public JWSRenewalInfoDecodedPayload renewalPrice(Long renewalPrice) {
402+
this.renewalPrice = renewalPrice;
403+
return this;
404+
}
405+
406+
/**
407+
* The renewal price, in milliunits, of the auto-renewable subscription that renews at the next billing period.
408+
*
409+
* @return renewalPrice
410+
* @see <a href="https://developer.apple.com/documentation/appstoreserverapi/renewalprice">renewalPrice</a>
411+
**/
412+
public Long getRenewalPrice() {
413+
return renewalPrice;
414+
}
415+
416+
public void setRenewalPrice(Long renewalPrice) {
417+
this.renewalPrice = renewalPrice;
418+
}
419+
420+
public JWSRenewalInfoDecodedPayload currency(String currency) {
421+
this.currency = currency;
422+
return this;
423+
}
424+
425+
/**
426+
* The currency code for the renewalPrice of the subscription.
427+
*
428+
* @return currency
429+
* @see <a href="https://developer.apple.com/documentation/appstoreserverapi/currency">currency</a>
430+
**/
431+
public String getCurrency() {
432+
return this.currency;
433+
}
434+
435+
public void setCurrency(String currency) {
436+
this.currency = currency;
437+
}
438+
439+
public JWSRenewalInfoDecodedPayload offerDiscountType(OfferDiscountType offerDiscountType) {
440+
this.offerDiscountType = offerDiscountType != null ? offerDiscountType.getValue() : null;
441+
return this;
442+
}
443+
444+
/**
445+
* The payment mode of the discount offer.
446+
*
447+
* @return offerDiscountType
448+
* @see <a href="https://developer.apple.com/documentation/appstoreserverapi/offerdiscounttype">offerDiscountType</a>
449+
**/
450+
public OfferDiscountType getOfferDiscountType() {
451+
return offerDiscountType != null ? OfferDiscountType.fromValue(offerDiscountType) : null;
452+
}
453+
454+
/**
455+
* @see #getOfferDiscountType()
456+
*/
457+
public String getRawOfferDiscountType() {
458+
return offerDiscountType;
459+
}
460+
461+
public void setOfferDiscountType(OfferDiscountType offerDiscountType) {
462+
this.offerDiscountType = offerDiscountType != null ? offerDiscountType.getValue() : null;
463+
}
464+
465+
public void setRawOfferDiscountType(String rawOfferDiscountType) {
466+
this.offerDiscountType = rawOfferDiscountType;
467+
}
392468

393469
public JWSRenewalInfoDecodedPayload unknownFields(Map<String, Object> unknownFields) {
394470
this.unknownFields = unknownFields;
@@ -431,12 +507,15 @@ public boolean equals(Object o) {
431507
Objects.equals(this.environment, jwSRenewalInfoDecodedPayload.environment) &&
432508
Objects.equals(this.recentSubscriptionStartDate, jwSRenewalInfoDecodedPayload.recentSubscriptionStartDate) &&
433509
Objects.equals(this.renewalDate, jwSRenewalInfoDecodedPayload.renewalDate) &&
510+
Objects.equals(this.renewalPrice, jwSRenewalInfoDecodedPayload.renewalPrice) &&
511+
Objects.equals(this.currency, jwSRenewalInfoDecodedPayload.currency) &&
512+
Objects.equals(this.offerDiscountType, jwSRenewalInfoDecodedPayload.offerDiscountType) &&
434513
Objects.equals(this.unknownFields, jwSRenewalInfoDecodedPayload.unknownFields);
435514
}
436515

437516
@Override
438517
public int hashCode() {
439-
return Objects.hash(expirationIntent, originalTransactionId, autoRenewProductId, productId, autoRenewStatus, isInBillingRetryPeriod, priceIncreaseStatus, gracePeriodExpiresDate, offerType, offerIdentifier, signedDate, environment, recentSubscriptionStartDate, renewalDate, unknownFields);
518+
return Objects.hash(expirationIntent, originalTransactionId, autoRenewProductId, productId, autoRenewStatus, isInBillingRetryPeriod, priceIncreaseStatus, gracePeriodExpiresDate, offerType, offerIdentifier, signedDate, environment, recentSubscriptionStartDate, renewalDate, renewalPrice, currency, offerDiscountType, unknownFields);
440519
}
441520

442521
@Override
@@ -456,6 +535,9 @@ public String toString() {
456535
", environment='" + environment + '\'' +
457536
", recentSubscriptionStartDate=" + recentSubscriptionStartDate +
458537
", renewalDate=" + renewalDate +
538+
", renewalPrice=" + renewalPrice +
539+
", currency='" + currency + '\'' +
540+
", offerDiscountType='" + offerDiscountType + '\'' +
459541
", unknownFields=" + unknownFields +
460542
'}';
461543
}

src/main/java/com/apple/itunes/storekit/model/NotificationTypeV2.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@ public enum NotificationTypeV2 {
2828
TEST("TEST"),
2929
RENEWAL_EXTENSION("RENEWAL_EXTENSION"),
3030
REFUND_REVERSED("REFUND_REVERSED"),
31-
EXTERNAL_PURCHASE_TOKEN("EXTERNAL_PURCHASE_TOKEN");
31+
EXTERNAL_PURCHASE_TOKEN("EXTERNAL_PURCHASE_TOKEN"),
32+
ONE_TIME_CHARGE("ONE_TIME_CHARGE");
3233

3334
private final String value;
3435

src/test/java/com/apple/itunes/storekit/client/AppStoreServerAPIClientTest.java

+42-3
Original file line numberDiff line numberDiff line change
@@ -310,7 +310,7 @@ public void testGetNotificationHistory() throws APIException, IOException {
310310
}
311311

312312
@Test
313-
public void testGetTransactionHistory() throws APIException, IOException {
313+
public void testGetTransactionHistoryV1() throws APIException, IOException {
314314
AppStoreServerAPIClient client = getClientWithBody("models/transactionHistoryResponse.json", request -> {
315315
Assertions.assertEquals("GET", request.method());
316316
Assertions.assertEquals("/inApps/v1/history/1234", request.url().encodedPath());
@@ -336,7 +336,46 @@ public void testGetTransactionHistory() throws APIException, IOException {
336336
.productIds(List.of("com.example.1", "com.example.2"))
337337
.subscriptionGroupIdentifiers(List.of("sub_group_id", "sub_group_id_2"));
338338

339-
HistoryResponse historyResponse = client.getTransactionHistory("1234", "revision_input", request);
339+
HistoryResponse historyResponse = client.getTransactionHistory("1234", "revision_input", request, GetTransactionHistoryVersion.V1);
340+
341+
Assertions.assertNotNull(historyResponse);
342+
Assertions.assertEquals("revision_output", historyResponse.getRevision());
343+
Assertions.assertTrue(historyResponse.getHasMore());
344+
Assertions.assertEquals("com.example", historyResponse.getBundleId());
345+
Assertions.assertEquals(323232L, historyResponse.getAppAppleId());
346+
Assertions.assertEquals(Environment.LOCAL_TESTING, historyResponse.getEnvironment());
347+
Assertions.assertEquals("LocalTesting", historyResponse.getRawEnvironment());
348+
Assertions.assertEquals(List.of("signed_transaction_value", "signed_transaction_value2"), historyResponse.getSignedTransactions());
349+
}
350+
351+
@Test
352+
public void testGetTransactionHistoryV2() throws APIException, IOException {
353+
AppStoreServerAPIClient client = getClientWithBody("models/transactionHistoryResponse.json", request -> {
354+
Assertions.assertEquals("GET", request.method());
355+
Assertions.assertEquals("/inApps/v2/history/1234", request.url().encodedPath());
356+
Assertions.assertEquals("revision_input", request.url().queryParameter("revision"));
357+
Assertions.assertEquals("123455", request.url().queryParameter("startDate"));
358+
Assertions.assertEquals("123456", request.url().queryParameter("endDate"));
359+
Assertions.assertEquals(List.of("com.example.1", "com.example.2"), request.url().queryParameterValues("productId"));
360+
Assertions.assertEquals(List.of("CONSUMABLE", "AUTO_RENEWABLE"), request.url().queryParameterValues("productType"));
361+
Assertions.assertEquals("ASCENDING", request.url().queryParameter("sort"));
362+
Assertions.assertEquals(List.of("sub_group_id", "sub_group_id_2"), request.url().queryParameterValues("subscriptionGroupIdentifier"));
363+
Assertions.assertEquals("FAMILY_SHARED", request.url().queryParameter("inAppOwnershipType"));
364+
Assertions.assertEquals("false", request.url().queryParameter("revoked"));
365+
Assertions.assertNull(request.body());
366+
});
367+
368+
TransactionHistoryRequest request = new TransactionHistoryRequest()
369+
.sort(TransactionHistoryRequest.Order.ASCENDING)
370+
.productTypes(List.of(TransactionHistoryRequest.ProductType.CONSUMABLE, TransactionHistoryRequest.ProductType.AUTO_RENEWABLE))
371+
.endDate(123456L)
372+
.startDate(123455L)
373+
.revoked(false)
374+
.inAppOwnershipType(InAppOwnershipType.FAMILY_SHARED)
375+
.productIds(List.of("com.example.1", "com.example.2"))
376+
.subscriptionGroupIdentifiers(List.of("sub_group_id", "sub_group_id_2"));
377+
378+
HistoryResponse historyResponse = client.getTransactionHistory("1234", "revision_input", request, GetTransactionHistoryVersion.V2);
340379

341380
Assertions.assertNotNull(historyResponse);
342381
Assertions.assertEquals("revision_output", historyResponse.getRevision());
@@ -581,7 +620,7 @@ public void testDecodingWithUnknownEnumValue() throws IOException, APIException
581620
.productIds(List.of("com.example.1", "com.example.2"))
582621
.subscriptionGroupIdentifiers(List.of("sub_group_id", "sub_group_id_2"));
583622

584-
HistoryResponse historyResponse = client.getTransactionHistory("1234", "revision_input", request);
623+
HistoryResponse historyResponse = client.getTransactionHistory("1234", "revision_input", request, GetTransactionHistoryVersion.V2);
585624

586625
Assertions.assertNull(historyResponse.getEnvironment());
587626
Assertions.assertEquals("LocalTestingxxx", historyResponse.getRawEnvironment());

src/test/java/com/apple/itunes/storekit/model/JWSRenewalInfoDecodedPayloadTest.java

+4
Original file line numberDiff line numberDiff line change
@@ -38,5 +38,9 @@ public void testRenewalInfoDecoding() throws IOException, NoSuchAlgorithmExcepti
3838
Assertions.assertEquals("LocalTesting", renewalInfo.getRawEnvironment());
3939
Assertions.assertEquals(1698148800000L, renewalInfo.getRecentSubscriptionStartDate());
4040
Assertions.assertEquals(1698148850000L, renewalInfo.getRenewalDate());
41+
Assertions.assertEquals(9990, renewalInfo.getRenewalPrice());
42+
Assertions.assertEquals("USD", renewalInfo.getCurrency());
43+
Assertions.assertEquals(OfferDiscountType.PAY_AS_YOU_GO, renewalInfo.getOfferDiscountType());
44+
Assertions.assertEquals("PAY_AS_YOU_GO", renewalInfo.getRawOfferDiscountType());
4145
}
4246
}

src/test/resources/models/signedRenewalInfo.json

+4-1
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,8 @@
1212
"signedDate": 1698148800000,
1313
"environment": "LocalTesting",
1414
"recentSubscriptionStartDate": 1698148800000,
15-
"renewalDate": 1698148850000
15+
"renewalDate": 1698148850000,
16+
"renewalPrice": 9990,
17+
"currency": "USD",
18+
"offerDiscountType": "PAY_AS_YOU_GO"
1619
}

0 commit comments

Comments
 (0)