diff --git a/src/main/java/com/apple/itunes/storekit/client/BaseAppStoreServerAPIClient.java b/src/main/java/com/apple/itunes/storekit/client/BaseAppStoreServerAPIClient.java
index ba4c36ea..90090e39 100644
--- a/src/main/java/com/apple/itunes/storekit/client/BaseAppStoreServerAPIClient.java
+++ b/src/main/java/com/apple/itunes/storekit/client/BaseAppStoreServerAPIClient.java
@@ -5,6 +5,7 @@
import com.apple.itunes.storekit.model.AppTransactionInfoResponse;
import com.apple.itunes.storekit.model.CheckTestNotificationResponse;
import com.apple.itunes.storekit.model.ConsumptionRequest;
+import com.apple.itunes.storekit.model.ConsumptionRequestV1;
import com.apple.itunes.storekit.model.DefaultConfigurationRequest;
import com.apple.itunes.storekit.model.Environment;
import com.apple.itunes.storekit.model.ErrorPayload;
@@ -365,12 +366,27 @@ public SendTestNotificationResponse requestTestNotification() throws APIExceptio
* @param consumptionRequest The request body containing consumption information.
* @throws APIException If a response was returned indicating the request could not be processed
* @throws IOException If an exception was thrown while making the request
- * @see Send Consumption Information
+ * @see Send Consumption Information V1
+ * @deprecated Use {@link #sendConsumptionInformation(String, ConsumptionRequest)} instead
*/
- public void sendConsumptionData(String transactionId, ConsumptionRequest consumptionRequest) throws APIException, IOException {
+ @Deprecated
+ public void sendConsumptionData(String transactionId, ConsumptionRequestV1 consumptionRequest) throws APIException, IOException {
makeHttpCall("/inApps/v1/transactions/consumption/" + transactionId, "PUT", Map.of(), consumptionRequest, Void.class, JSON);
}
+ /**
+ * Send consumption information about an In-App Purchase to the App Store after your server receives a consumption request notification.
+ *
+ * @param transactionId The transaction identifier for which you’re providing consumption information. You receive this identifier in the CONSUMPTION_REQUEST notification the App Store sends to your server’s App Store Server Notifications V2 endpoint.
+ * @param consumptionRequest The request body containing consumption information.
+ * @throws APIException If a response was returned indicating the request could not be processed
+ * @throws IOException If an exception was thrown while making the request
+ * @see Send Consumption Information
+ */
+ public void sendConsumptionInformation(String transactionId, ConsumptionRequest consumptionRequest) throws APIException, IOException {
+ makeHttpCall("/inApps/v2/transactions/consumption/" + transactionId, "PUT", Map.of(), consumptionRequest, Void.class, JSON);
+ }
+
/**
* Sets the app account token value for a purchase the customer makes outside your app, or updates its value in an existing transaction.
diff --git a/src/main/java/com/apple/itunes/storekit/model/AppData.java b/src/main/java/com/apple/itunes/storekit/model/AppData.java
new file mode 100644
index 00000000..795d4cd0
--- /dev/null
+++ b/src/main/java/com/apple/itunes/storekit/model/AppData.java
@@ -0,0 +1,172 @@
+// Copyright (c) 2025 Apple Inc. Licensed under MIT License.
+
+package com.apple.itunes.storekit.model;
+
+import com.fasterxml.jackson.annotation.JsonAnySetter;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * The object that contains the app metadata and signed app transaction information.
+ *
+ * @see appData
+ */
+public class AppData {
+ private static final String SERIALIZED_NAME_APP_APPLE_ID = "appAppleId";
+ private static final String SERIALIZED_NAME_BUNDLE_ID = "bundleId";
+ private static final String SERIALIZED_NAME_ENVIRONMENT = "environment";
+ private static final String SERIALIZED_NAME_SIGNED_APP_TRANSACTION_INFO = "signedAppTransactionInfo";
+ @JsonProperty(SERIALIZED_NAME_APP_APPLE_ID)
+ private Long appAppleId;
+ @JsonProperty(SERIALIZED_NAME_BUNDLE_ID)
+ private String bundleId;
+ @JsonProperty(SERIALIZED_NAME_ENVIRONMENT)
+ private String environment;
+ @JsonProperty(SERIALIZED_NAME_SIGNED_APP_TRANSACTION_INFO)
+ private String signedAppTransactionInfo;
+ @JsonAnySetter
+ private Map unknownFields;
+
+
+ public AppData() {
+ }
+
+ public AppData appAppleId(Long appAppleId) {
+ this.appAppleId = appAppleId;
+ return this;
+ }
+
+ /**
+ * The unique identifier of the app that the notification applies to.
+ *
+ * @return appAppleId
+ * @see appAppleId
+ **/
+ public Long getAppAppleId() {
+ return appAppleId;
+ }
+
+ public void setAppAppleId(Long appAppleId) {
+ this.appAppleId = appAppleId;
+ }
+
+ public AppData bundleId(String bundleId) {
+ this.bundleId = bundleId;
+ return this;
+ }
+
+ /**
+ * The bundle identifier of the app.
+ *
+ * @return bundleId
+ * @see bundleId
+ **/
+ public String getBundleId() {
+ return bundleId;
+ }
+
+ public void setBundleId(String bundleId) {
+ this.bundleId = bundleId;
+ }
+
+ public AppData environment(Environment environment) {
+ this.environment = environment != null ? environment.getValue() : null;
+ return this;
+ }
+
+ /**
+ * The server environment that the notification applies to, either sandbox or production.
+ *
+ * @return environment
+ * @see environment
+ **/
+ public Environment getEnvironment() {
+ return environment != null ? Environment.fromValue(environment) : null;
+ }
+
+ /**
+ * @see #getEnvironment()
+ */
+ public String getRawEnvironment() {
+ return environment;
+ }
+
+ public void setEnvironment(Environment environment) {
+ this.environment = environment != null ? environment.getValue() : null;
+ }
+
+ public void setRawEnvironment(String rawEnvironment) {
+ this.environment = rawEnvironment;
+ }
+
+ public AppData signedAppTransactionInfo(String signedAppTransactionInfo) {
+ this.signedAppTransactionInfo = signedAppTransactionInfo;
+ return this;
+ }
+
+ /**
+ * App transaction information signed by the App Store, in JSON Web Signature (JWS) format.
+ *
+ * @return signedAppTransactionInfo
+ * @see JWSAppTransaction
+ **/
+ public String getSignedAppTransactionInfo() {
+ return signedAppTransactionInfo;
+ }
+
+ public void setSignedAppTransactionInfo(String signedAppTransactionInfo) {
+ this.signedAppTransactionInfo = signedAppTransactionInfo;
+ }
+
+ public AppData unknownFields(Map unknownFields) {
+ this.unknownFields = unknownFields;
+ return this;
+ }
+
+ /**
+ Fields that are not recognized for this object
+
+ @return A map of JSON keys to objects
+ */
+ public Map getUnknownFields() {
+ return unknownFields;
+ }
+
+ public void setUnknownFields(Map unknownFields) {
+ this.unknownFields = unknownFields;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ AppData appData = (AppData) o;
+ return Objects.equals(this.appAppleId, appData.appAppleId) &&
+ Objects.equals(this.bundleId, appData.bundleId) &&
+ Objects.equals(this.environment, appData.environment) &&
+ Objects.equals(this.signedAppTransactionInfo, appData.signedAppTransactionInfo) &&
+ Objects.equals(this.unknownFields, appData.unknownFields);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(appAppleId, bundleId, environment, signedAppTransactionInfo, unknownFields);
+ }
+
+ @Override
+ public String toString() {
+ return "AppData{" +
+ "appAppleId=" + appAppleId +
+ ", bundleId='" + bundleId + '\'' +
+ ", environment='" + environment + '\'' +
+ ", signedAppTransactionInfo='" + signedAppTransactionInfo + '\'' +
+ ", unknownFields=" + unknownFields +
+ '}';
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/apple/itunes/storekit/model/ConsumptionRequest.java b/src/main/java/com/apple/itunes/storekit/model/ConsumptionRequest.java
index 9c9bf8ee..943f4bf7 100644
--- a/src/main/java/com/apple/itunes/storekit/model/ConsumptionRequest.java
+++ b/src/main/java/com/apple/itunes/storekit/model/ConsumptionRequest.java
@@ -1,64 +1,52 @@
-// Copyright (c) 2023 Apple Inc. Licensed under MIT License.
+// Copyright (c) 2025 Apple Inc. Licensed under MIT License.
package com.apple.itunes.storekit.model;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
-import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import java.util.Objects;
-import java.util.UUID;
/**
- * The request body containing consumption information.
+ * The request body that contains consumption information for an In-App Purchase.
*
* @see ConsumptionRequest
*/
@JsonInclude(JsonInclude.Include.NON_NULL)
public class ConsumptionRequest {
private static final String SERIALIZED_NAME_CUSTOMER_CONSENTED = "customerConsented";
- private static final String SERIALIZED_NAME_CONSUMPTION_STATUS = "consumptionStatus";
- private static final String SERIALIZED_NAME_PLATFORM = "platform";
- private static final String SERIALIZED_NAME_SAMPLE_CONTENT_PROVIDED = "sampleContentProvided";
+ private static final String SERIALIZED_NAME_CONSUMPTION_PERCENTAGE = "consumptionPercentage";
private static final String SERIALIZED_NAME_DELIVERY_STATUS = "deliveryStatus";
- private static final String SERIALIZED_NAME_APP_ACCOUNT_TOKEN = "appAccountToken";
- private static final String SERIALIZED_NAME_ACCOUNT_TENURE = "accountTenure";
- private static final String SERIALIZED_NAME_PLAY_TIME = "playTime";
- private static final String SERIALIZED_NAME_LIFETIME_DOLLARS_REFUNDED = "lifetimeDollarsRefunded";
- private static final String SERIALIZED_NAME_LIFETIME_DOLLARS_PURCHASED = "lifetimeDollarsPurchased";
- private static final String SERIALIZED_NAME_USER_STATUS = "userStatus";
private static final String SERIALIZED_NAME_REFUND_PREFERENCE = "refundPreference";
- @JsonProperty(SERIALIZED_NAME_CUSTOMER_CONSENTED)
- private Boolean customerConsented;
- @JsonProperty(SERIALIZED_NAME_CONSUMPTION_STATUS)
- private Integer consumptionStatus;
- @JsonProperty(SERIALIZED_NAME_PLATFORM)
- private Integer platform;
- @JsonProperty(SERIALIZED_NAME_SAMPLE_CONTENT_PROVIDED)
- private Boolean sampleContentProvided;
- @JsonProperty(SERIALIZED_NAME_DELIVERY_STATUS)
- private Integer deliveryStatus;
- @JsonProperty(SERIALIZED_NAME_APP_ACCOUNT_TOKEN)
- @JsonSerialize(nullsUsing = AppAccountTokenNullSerializer.class)
- private UUID appAccountToken;
- @JsonProperty(SERIALIZED_NAME_ACCOUNT_TENURE)
- private Integer accountTenure;
- @JsonProperty(SERIALIZED_NAME_PLAY_TIME)
- private Integer playTime;
- @JsonProperty(SERIALIZED_NAME_LIFETIME_DOLLARS_REFUNDED)
- private Integer lifetimeDollarsRefunded;
- @JsonProperty(SERIALIZED_NAME_LIFETIME_DOLLARS_PURCHASED)
- private Integer lifetimeDollarsPurchased;
- @JsonProperty(SERIALIZED_NAME_USER_STATUS)
- private Integer userStatus;
+ private static final String SERIALIZED_NAME_SAMPLE_CONTENT_PROVIDED = "sampleContentProvided";
+
+ @JsonProperty(value = SERIALIZED_NAME_CUSTOMER_CONSENTED, required = true)
+ private boolean customerConsented;
+ @JsonProperty(SERIALIZED_NAME_CONSUMPTION_PERCENTAGE)
+ private Integer consumptionPercentage;
+ @JsonProperty(value = SERIALIZED_NAME_DELIVERY_STATUS, required = true)
+ private String deliveryStatus;
@JsonProperty(SERIALIZED_NAME_REFUND_PREFERENCE)
- private Integer refundPreference;
+ private String refundPreference;
+ @JsonProperty(value = SERIALIZED_NAME_SAMPLE_CONTENT_PROVIDED, required = true)
+ private boolean sampleContentProvided;
+
+ private ConsumptionRequest() {
+ }
+ public ConsumptionRequest(boolean customerConsented, DeliveryStatus deliveryStatus, boolean sampleContentProvided) {
+ this.customerConsented = customerConsented;
+ this.deliveryStatus = Objects.requireNonNull(deliveryStatus).getValue();
+ this.sampleContentProvided = sampleContentProvided;
+ }
- public ConsumptionRequest() {
+ public ConsumptionRequest(boolean customerConsented, String rawDeliveryStatus, boolean sampleContentProvided) {
+ this.customerConsented = customerConsented;
+ this.deliveryStatus = Objects.requireNonNull(rawDeliveryStatus);
+ this.sampleContentProvided = sampleContentProvided;
}
- public ConsumptionRequest customerConsented(Boolean customerConsented) {
+ public ConsumptionRequest customerConsented(boolean customerConsented) {
this.customerConsented = customerConsented;
return this;
}
@@ -69,95 +57,35 @@ public ConsumptionRequest customerConsented(Boolean customerConsented) {
* @return customerConsented
* @see customerConsented
**/
- public Boolean getCustomerConsented() {
+ public boolean getCustomerConsented() {
return customerConsented;
}
- public void setCustomerConsented(Boolean customerConsented) {
+ public void setCustomerConsented(boolean customerConsented) {
this.customerConsented = customerConsented;
}
- public ConsumptionRequest consumptionStatus(ConsumptionStatus consumptionStatus) {
- this.consumptionStatus = consumptionStatus != null ? consumptionStatus.getValue() : null;
- return this;
- }
-
- /**
- * A value that indicates the extent to which the customer consumed the in-app purchase.
- *
- * @return consumptionStatus
- * @see consumptionStatus
- **/
- public ConsumptionStatus getConsumptionStatus() {
- return consumptionStatus != null ? ConsumptionStatus.fromValue(consumptionStatus) : null;
- }
-
- /**
- * @see #getConsumptionStatus()
- */
- public Integer getRawConsumptionStatus() {
- return consumptionStatus;
- }
-
- public void setConsumptionStatus(ConsumptionStatus consumptionStatus) {
- this.consumptionStatus = consumptionStatus != null ? consumptionStatus.getValue() : null;
- }
-
- public void setRawConsumptionStatus(Integer rawConsumptionStatus) {
- this.consumptionStatus = rawConsumptionStatus;
- }
-
- public ConsumptionRequest platform(Platform platform) {
- this.platform = platform != null ? platform.getValue() : null;
- return this;
- }
-
- /**
- * A value that indicates the platform on which the customer consumed the in-app purchase.
- *
- * @return platform
- * @see platform
- **/
- public Platform getPlatform() {
- return platform != null ? Platform.fromValue(platform) : null;
- }
-
- /**
- * @see #getPlatform()
- */
- public Integer getRawPlatform() {
- return platform;
- }
-
- public void setPlatform(Platform platform) {
- this.platform = platform != null ? platform.getValue() : null;
- }
-
- public void setRawPlatform(Integer rawPlatform) {
- this.platform = rawPlatform;
- }
-
- public ConsumptionRequest sampleContentProvided(Boolean sampleContentProvided) {
- this.sampleContentProvided = sampleContentProvided;
+ public ConsumptionRequest consumptionPercentage(Integer consumptionPercentage) {
+ this.consumptionPercentage = consumptionPercentage;
return this;
}
/**
- * A Boolean value that indicates whether you provided, prior to its purchase, a free sample or trial of the content, or information about its functionality.
+ * An integer that indicates the percentage, in milliunits, of the In-App Purchase the customer consumed.
*
- * @return sampleContentProvided
- * @see sampleContentProvided
+ * @return consumptionPercentage
+ * @see consumptionPercentage
**/
- public Boolean getSampleContentProvided() {
- return sampleContentProvided;
+ public Integer getConsumptionPercentage() {
+ return consumptionPercentage;
}
- public void setSampleContentProvided(Boolean sampleContentProvided) {
- this.sampleContentProvided = sampleContentProvided;
+ public void setConsumptionPercentage(Integer consumptionPercentage) {
+ this.consumptionPercentage = consumptionPercentage;
}
public ConsumptionRequest deliveryStatus(DeliveryStatus deliveryStatus) {
- this.deliveryStatus = deliveryStatus != null ? deliveryStatus.getValue() : null;
+ this.deliveryStatus = Objects.requireNonNull(deliveryStatus).getValue();
return this;
}
@@ -168,221 +96,71 @@ public ConsumptionRequest deliveryStatus(DeliveryStatus deliveryStatus) {
* @see deliveryStatus
**/
public DeliveryStatus getDeliveryStatus() {
- return deliveryStatus != null ? DeliveryStatus.fromValue(deliveryStatus) : null;
+ return DeliveryStatus.fromValue(deliveryStatus);
}
/**
* @see #getDeliveryStatus()
*/
- public Integer getRawDeliveryStatus() {
+ public String getRawDeliveryStatus() {
return deliveryStatus;
}
public void setDeliveryStatus(DeliveryStatus deliveryStatus) {
- this.deliveryStatus = deliveryStatus != null ? deliveryStatus.getValue() : null;
- }
-
- public void setRawDeliveryStatus(Integer rawDeliveryStatus) {
- this.deliveryStatus = rawDeliveryStatus;
- }
-
- public ConsumptionRequest appAccountToken(UUID appAccountToken) {
- this.appAccountToken = appAccountToken;
- return this;
- }
-
- /**
- * The UUID that an app optionally generates to map a customer’s in-app purchase with its resulting App Store transaction.
- *
- * @return appAccountToken
- * @see appAccountToken
- **/
- public UUID getAppAccountToken() {
- return appAccountToken;
- }
-
- public void setAppAccountToken(UUID appAccountToken) {
- this.appAccountToken = appAccountToken;
- }
-
- public ConsumptionRequest accountTenure(AccountTenure accountTenure) {
- this.accountTenure = accountTenure != null ? accountTenure.getValue() : null;
- return this;
- }
-
- /**
- * The age of the customer’s account.
- *
- * @return accountTenure
- * @see accountTenure
- **/
- public AccountTenure getAccountTenure() {
- return accountTenure != null ? AccountTenure.fromValue(accountTenure) : null;
- }
-
- /**
- * @see #getAccountTenure()
- */
- public Integer getRawAccountTenure() {
- return accountTenure;
- }
-
- public void setAccountTenure(AccountTenure accountTenure) {
- this.accountTenure = accountTenure != null ? accountTenure.getValue() : null;
- }
-
- public void setRawAccountTenure(Integer rawAccountTenure) {
- this.accountTenure = rawAccountTenure;
- }
-
- public ConsumptionRequest playTime(PlayTime playTime) {
- this.playTime = playTime != null ? playTime.getValue() : null;
- return this;
- }
-
- /**
- * A value that indicates the amount of time that the customer used the app.
- *
- * @return playTime
- * @see playTime
- **/
- public PlayTime getPlayTime() {
- return playTime != null ? PlayTime.fromValue(playTime) : null;
- }
-
- /**
- * @see #getPlayTime()
- */
- public Integer getRawPlayTime() {
- return playTime;
- }
-
- public void setPlayTime(PlayTime playTime) {
- this.playTime = playTime != null ? playTime.getValue() : null;
- }
-
- public void setRawPlayTime(Integer rawPlayTime) {
- this.playTime = rawPlayTime;
- }
-
- public ConsumptionRequest lifetimeDollarsRefunded(LifetimeDollarsRefunded lifetimeDollarsRefunded) {
- this.lifetimeDollarsRefunded = lifetimeDollarsRefunded != null ? lifetimeDollarsRefunded.getValue() : null;
- return this;
- }
-
- /**
- * A value that indicates the total amount, in USD, of refunds the customer has received, in your app, across all platforms.
- *
- * @return lifetimeDollarsRefunded
- * @see lifetimeDollarsRefunded
- **/
- public LifetimeDollarsRefunded getLifetimeDollarsRefunded() {
- return lifetimeDollarsRefunded != null ? LifetimeDollarsRefunded.fromValue(lifetimeDollarsRefunded) : null;
- }
-
- /**
- * @see #getLifetimeDollarsRefunded()
- */
- public Integer getRawLifetimeDollarsRefunded() {
- return lifetimeDollarsRefunded;
- }
-
- public void setLifetimeDollarsRefunded(LifetimeDollarsRefunded lifetimeDollarsRefunded) {
- this.lifetimeDollarsRefunded = lifetimeDollarsRefunded != null ? lifetimeDollarsRefunded.getValue() : null;
- }
-
- public void setRawLifetimeDollarsRefunded(Integer rawLifetimeDollarsRefunded) {
- this.lifetimeDollarsRefunded = rawLifetimeDollarsRefunded;
- }
-
- public ConsumptionRequest lifetimeDollarsPurchased(LifetimeDollarsPurchased lifetimeDollarsPurchased) {
- this.lifetimeDollarsPurchased = lifetimeDollarsPurchased != null ? lifetimeDollarsPurchased.getValue() : null;
- return this;
- }
-
- /**
- * A value that indicates the total amount, in USD, of in-app purchases the customer has made in your app, across all platforms.
- *
- * @return lifetimeDollarsPurchased
- * @see lifetimeDollarsPurchased
- **/
- public LifetimeDollarsPurchased getLifetimeDollarsPurchased() {
- return lifetimeDollarsPurchased != null ? LifetimeDollarsPurchased.fromValue(lifetimeDollarsPurchased) : null;
- }
-
- /**
- * @see #getLifetimeDollarsPurchased()
- */
- public Integer getRawLifetimeDollarsPurchased() {
- return lifetimeDollarsPurchased;
- }
-
- public void setLifetimeDollarsPurchased(LifetimeDollarsPurchased lifetimeDollarsPurchased) {
- this.lifetimeDollarsPurchased = lifetimeDollarsPurchased != null ? lifetimeDollarsPurchased.getValue() : null;
+ this.deliveryStatus = Objects.requireNonNull(deliveryStatus).getValue();
}
- public void setRawLifetimeDollarsPurchased(Integer rawLifetimeDollarsPurchased) {
- this.lifetimeDollarsPurchased = rawLifetimeDollarsPurchased;
+ public void setRawDeliveryStatus(String rawDeliveryStatus) {
+ this.deliveryStatus = Objects.requireNonNull(rawDeliveryStatus);
}
- public ConsumptionRequest userStatus(UserStatus userStatus) {
- this.userStatus = userStatus != null ? userStatus.getValue() : null;
+ public ConsumptionRequest refundPreference(RefundPreference refundPreference) {
+ this.refundPreference = refundPreference != null ? refundPreference.getValue() : null;
return this;
}
/**
- * The status of the customer’s account.
+ * A value that indicates your preferred outcome for the refund request.
*
- * @return userStatus
- * @see userStatus
+ * @return refundPreference
+ * @see refundPreference
**/
- public UserStatus getUserStatus() {
- return userStatus != null ? UserStatus.fromValue(userStatus) : null;
+ public RefundPreference getRefundPreference() {
+ return refundPreference != null ? RefundPreference.fromValue(refundPreference) : null;
}
/**
- * @see #getUserStatus()
+ * @see #getRefundPreference()
*/
- public Integer getRawUserStatus() {
- return userStatus;
+ public String getRawRefundPreference() {
+ return refundPreference;
}
- public void setUserStatus(UserStatus userStatus) {
- this.userStatus = userStatus != null ? userStatus.getValue() : null;
+ public void setRefundPreference(RefundPreference refundPreference) {
+ this.refundPreference = Objects.requireNonNull(refundPreference).getValue();
}
- public void setRawUserStatus(Integer rawUserStatus) {
- this.userStatus = rawUserStatus;
+ public void setRawRefundPreference(String rawRefundPreference) {
+ this.refundPreference = Objects.requireNonNull(rawRefundPreference);
}
- public ConsumptionRequest refundPreference(RefundPreference refundPreference) {
- this.refundPreference = refundPreference != null ? refundPreference.getValue() : null;
+ public ConsumptionRequest sampleContentProvided(boolean sampleContentProvided) {
+ this.sampleContentProvided = sampleContentProvided;
return this;
}
/**
- * A value that indicates your preference, based on your operational logic, as to whether Apple should grant the refund.
+ * A Boolean value that indicates whether you provided, prior to its purchase, a free sample or trial of the content, or information about its functionality.
*
- * @return refundPreference
- * @see refundPreference
+ * @return sampleContentProvided
+ * @see sampleContentProvided
**/
- public RefundPreference getRefundPreference() {
- return refundPreference != null ? RefundPreference.fromValue(refundPreference) : null;
- }
-
- /**
- * @see #getRefundPreference()
- */
- public Integer getRawRefundPreference() {
- return refundPreference;
- }
-
- public void setRefundPreference(RefundPreference refundPreference) {
- this.refundPreference = refundPreference != null ? refundPreference.getValue() : null;
+ public boolean getSampleContentProvided() {
+ return sampleContentProvided;
}
- public void setRawRefundPreference(Integer rawRefundPreference) {
- this.refundPreference = rawRefundPreference;
+ public void setSampleContentProvided(boolean sampleContentProvided) {
+ this.sampleContentProvided = sampleContentProvided;
}
@Override
@@ -394,41 +172,26 @@ public boolean equals(Object o) {
return false;
}
ConsumptionRequest consumptionRequest = (ConsumptionRequest) o;
- return Objects.equals(this.customerConsented, consumptionRequest.customerConsented) &&
- Objects.equals(this.consumptionStatus, consumptionRequest.consumptionStatus) &&
- Objects.equals(this.platform, consumptionRequest.platform) &&
- Objects.equals(this.sampleContentProvided, consumptionRequest.sampleContentProvided) &&
+ return (this.customerConsented == consumptionRequest.customerConsented) &&
+ Objects.equals(this.consumptionPercentage, consumptionRequest.consumptionPercentage) &&
Objects.equals(this.deliveryStatus, consumptionRequest.deliveryStatus) &&
- Objects.equals(this.appAccountToken, consumptionRequest.appAccountToken) &&
- Objects.equals(this.accountTenure, consumptionRequest.accountTenure) &&
- Objects.equals(this.playTime, consumptionRequest.playTime) &&
- Objects.equals(this.lifetimeDollarsRefunded, consumptionRequest.lifetimeDollarsRefunded) &&
- Objects.equals(this.lifetimeDollarsPurchased, consumptionRequest.lifetimeDollarsPurchased) &&
- Objects.equals(this.userStatus, consumptionRequest.userStatus) &&
- Objects.equals(this.refundPreference, consumptionRequest.refundPreference);
+ Objects.equals(this.refundPreference, consumptionRequest.refundPreference) &&
+ (this.sampleContentProvided == consumptionRequest.sampleContentProvided);
}
@Override
public int hashCode() {
- return Objects.hash(customerConsented, consumptionStatus, platform, sampleContentProvided, deliveryStatus, appAccountToken, accountTenure, playTime, lifetimeDollarsRefunded, lifetimeDollarsPurchased, userStatus, refundPreference);
+ return Objects.hash(customerConsented, consumptionPercentage, deliveryStatus, refundPreference, sampleContentProvided);
}
@Override
public String toString() {
return "ConsumptionRequest{" +
"customerConsented=" + customerConsented +
- ", consumptionStatus=" + consumptionStatus +
- ", platform=" + platform +
- ", sampleContentProvided=" + sampleContentProvided +
+ ", consumptionPercentage=" + consumptionPercentage +
", deliveryStatus=" + deliveryStatus +
- ", appAccountToken=" + appAccountToken +
- ", accountTenure=" + accountTenure +
- ", playTime=" + playTime +
- ", lifetimeDollarsRefunded=" + lifetimeDollarsRefunded +
- ", lifetimeDollarsPurchased=" + lifetimeDollarsPurchased +
- ", userStatus=" + userStatus +
", refundPreference=" + refundPreference +
+ ", sampleContentProvided=" + sampleContentProvided +
'}';
}
}
-
diff --git a/src/main/java/com/apple/itunes/storekit/model/ConsumptionRequestV1.java b/src/main/java/com/apple/itunes/storekit/model/ConsumptionRequestV1.java
new file mode 100644
index 00000000..91f3174a
--- /dev/null
+++ b/src/main/java/com/apple/itunes/storekit/model/ConsumptionRequestV1.java
@@ -0,0 +1,435 @@
+// Copyright (c) 2023 Apple Inc. Licensed under MIT License.
+
+package com.apple.itunes.storekit.model;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+
+import java.util.Objects;
+import java.util.UUID;
+
+/**
+ * The request body containing consumption information.
+ *
+ * @see ConsumptionRequestV1
+ * @deprecated Use {@link ConsumptionRequest} instead.
+ */
+@Deprecated
+@JsonInclude(JsonInclude.Include.NON_NULL)
+public class ConsumptionRequestV1 {
+ private static final String SERIALIZED_NAME_CUSTOMER_CONSENTED = "customerConsented";
+ private static final String SERIALIZED_NAME_CONSUMPTION_STATUS = "consumptionStatus";
+ private static final String SERIALIZED_NAME_PLATFORM = "platform";
+ private static final String SERIALIZED_NAME_SAMPLE_CONTENT_PROVIDED = "sampleContentProvided";
+ private static final String SERIALIZED_NAME_DELIVERY_STATUS = "deliveryStatus";
+ private static final String SERIALIZED_NAME_APP_ACCOUNT_TOKEN = "appAccountToken";
+ private static final String SERIALIZED_NAME_ACCOUNT_TENURE = "accountTenure";
+ private static final String SERIALIZED_NAME_PLAY_TIME = "playTime";
+ private static final String SERIALIZED_NAME_LIFETIME_DOLLARS_REFUNDED = "lifetimeDollarsRefunded";
+ private static final String SERIALIZED_NAME_LIFETIME_DOLLARS_PURCHASED = "lifetimeDollarsPurchased";
+ private static final String SERIALIZED_NAME_USER_STATUS = "userStatus";
+ private static final String SERIALIZED_NAME_REFUND_PREFERENCE = "refundPreference";
+ @JsonProperty(SERIALIZED_NAME_CUSTOMER_CONSENTED)
+ private Boolean customerConsented;
+ @JsonProperty(SERIALIZED_NAME_CONSUMPTION_STATUS)
+ private Integer consumptionStatus;
+ @JsonProperty(SERIALIZED_NAME_PLATFORM)
+ private Integer platform;
+ @JsonProperty(SERIALIZED_NAME_SAMPLE_CONTENT_PROVIDED)
+ private Boolean sampleContentProvided;
+ @JsonProperty(SERIALIZED_NAME_DELIVERY_STATUS)
+ private Integer deliveryStatus;
+ @JsonProperty(SERIALIZED_NAME_APP_ACCOUNT_TOKEN)
+ @JsonSerialize(nullsUsing = AppAccountTokenNullSerializer.class)
+ private UUID appAccountToken;
+ @JsonProperty(SERIALIZED_NAME_ACCOUNT_TENURE)
+ private Integer accountTenure;
+ @JsonProperty(SERIALIZED_NAME_PLAY_TIME)
+ private Integer playTime;
+ @JsonProperty(SERIALIZED_NAME_LIFETIME_DOLLARS_REFUNDED)
+ private Integer lifetimeDollarsRefunded;
+ @JsonProperty(SERIALIZED_NAME_LIFETIME_DOLLARS_PURCHASED)
+ private Integer lifetimeDollarsPurchased;
+ @JsonProperty(SERIALIZED_NAME_USER_STATUS)
+ private Integer userStatus;
+ @JsonProperty(SERIALIZED_NAME_REFUND_PREFERENCE)
+ private Integer refundPreference;
+
+
+ public ConsumptionRequestV1() {
+ }
+
+ public ConsumptionRequestV1 customerConsented(Boolean customerConsented) {
+ this.customerConsented = customerConsented;
+ return this;
+ }
+
+ /**
+ * A Boolean value that indicates whether the customer consented to provide consumption data to the App Store.
+ *
+ * @return customerConsented
+ * @see customerConsented
+ **/
+ public Boolean getCustomerConsented() {
+ return customerConsented;
+ }
+
+ public void setCustomerConsented(Boolean customerConsented) {
+ this.customerConsented = customerConsented;
+ }
+
+ public ConsumptionRequestV1 consumptionStatus(ConsumptionStatus consumptionStatus) {
+ this.consumptionStatus = consumptionStatus != null ? consumptionStatus.getValue() : null;
+ return this;
+ }
+
+ /**
+ * A value that indicates the extent to which the customer consumed the in-app purchase.
+ *
+ * @return consumptionStatus
+ * @see consumptionStatus
+ **/
+ public ConsumptionStatus getConsumptionStatus() {
+ return consumptionStatus != null ? ConsumptionStatus.fromValue(consumptionStatus) : null;
+ }
+
+ /**
+ * @see #getConsumptionStatus()
+ */
+ public Integer getRawConsumptionStatus() {
+ return consumptionStatus;
+ }
+
+ public void setConsumptionStatus(ConsumptionStatus consumptionStatus) {
+ this.consumptionStatus = consumptionStatus != null ? consumptionStatus.getValue() : null;
+ }
+
+ public void setRawConsumptionStatus(Integer rawConsumptionStatus) {
+ this.consumptionStatus = rawConsumptionStatus;
+ }
+
+ public ConsumptionRequestV1 platform(Platform platform) {
+ this.platform = platform != null ? platform.getValue() : null;
+ return this;
+ }
+
+ /**
+ * A value that indicates the platform on which the customer consumed the in-app purchase.
+ *
+ * @return platform
+ * @see platform
+ **/
+ public Platform getPlatform() {
+ return platform != null ? Platform.fromValue(platform) : null;
+ }
+
+ /**
+ * @see #getPlatform()
+ */
+ public Integer getRawPlatform() {
+ return platform;
+ }
+
+ public void setPlatform(Platform platform) {
+ this.platform = platform != null ? platform.getValue() : null;
+ }
+
+ public void setRawPlatform(Integer rawPlatform) {
+ this.platform = rawPlatform;
+ }
+
+ public ConsumptionRequestV1 sampleContentProvided(Boolean sampleContentProvided) {
+ this.sampleContentProvided = sampleContentProvided;
+ return this;
+ }
+
+ /**
+ * A Boolean value that indicates whether you provided, prior to its purchase, a free sample or trial of the content, or information about its functionality.
+ *
+ * @return sampleContentProvided
+ * @see sampleContentProvided
+ **/
+ public Boolean getSampleContentProvided() {
+ return sampleContentProvided;
+ }
+
+ public void setSampleContentProvided(Boolean sampleContentProvided) {
+ this.sampleContentProvided = sampleContentProvided;
+ }
+
+ public ConsumptionRequestV1 deliveryStatus(DeliveryStatusV1 deliveryStatus) {
+ this.deliveryStatus = deliveryStatus != null ? deliveryStatus.getValue() : null;
+ return this;
+ }
+
+ /**
+ * A value that indicates whether the app successfully delivered an in-app purchase that works properly.
+ *
+ * @return deliveryStatus
+ * @see deliveryStatusV1
+ **/
+ public DeliveryStatusV1 getDeliveryStatus() {
+ return deliveryStatus != null ? DeliveryStatusV1.fromValue(deliveryStatus) : null;
+ }
+
+ /**
+ * @see #getDeliveryStatus()
+ */
+ public Integer getRawDeliveryStatus() {
+ return deliveryStatus;
+ }
+
+ public void setDeliveryStatus(DeliveryStatusV1 deliveryStatus) {
+ this.deliveryStatus = deliveryStatus != null ? deliveryStatus.getValue() : null;
+ }
+
+ public void setRawDeliveryStatus(Integer rawDeliveryStatus) {
+ this.deliveryStatus = rawDeliveryStatus;
+ }
+
+ public ConsumptionRequestV1 appAccountToken(UUID appAccountToken) {
+ this.appAccountToken = appAccountToken;
+ return this;
+ }
+
+ /**
+ * The UUID that an app optionally generates to map a customer’s in-app purchase with its resulting App Store transaction.
+ *
+ * @return appAccountToken
+ * @see appAccountToken
+ **/
+ public UUID getAppAccountToken() {
+ return appAccountToken;
+ }
+
+ public void setAppAccountToken(UUID appAccountToken) {
+ this.appAccountToken = appAccountToken;
+ }
+
+ public ConsumptionRequestV1 accountTenure(AccountTenure accountTenure) {
+ this.accountTenure = accountTenure != null ? accountTenure.getValue() : null;
+ return this;
+ }
+
+ /**
+ * The age of the customer’s account.
+ *
+ * @return accountTenure
+ * @see accountTenure
+ **/
+ public AccountTenure getAccountTenure() {
+ return accountTenure != null ? AccountTenure.fromValue(accountTenure) : null;
+ }
+
+ /**
+ * @see #getAccountTenure()
+ */
+ public Integer getRawAccountTenure() {
+ return accountTenure;
+ }
+
+ public void setAccountTenure(AccountTenure accountTenure) {
+ this.accountTenure = accountTenure != null ? accountTenure.getValue() : null;
+ }
+
+ public void setRawAccountTenure(Integer rawAccountTenure) {
+ this.accountTenure = rawAccountTenure;
+ }
+
+ public ConsumptionRequestV1 playTime(PlayTime playTime) {
+ this.playTime = playTime != null ? playTime.getValue() : null;
+ return this;
+ }
+
+ /**
+ * A value that indicates the amount of time that the customer used the app.
+ *
+ * @return playTime
+ * @see playTime
+ **/
+ public PlayTime getPlayTime() {
+ return playTime != null ? PlayTime.fromValue(playTime) : null;
+ }
+
+ /**
+ * @see #getPlayTime()
+ */
+ public Integer getRawPlayTime() {
+ return playTime;
+ }
+
+ public void setPlayTime(PlayTime playTime) {
+ this.playTime = playTime != null ? playTime.getValue() : null;
+ }
+
+ public void setRawPlayTime(Integer rawPlayTime) {
+ this.playTime = rawPlayTime;
+ }
+
+ public ConsumptionRequestV1 lifetimeDollarsRefunded(LifetimeDollarsRefunded lifetimeDollarsRefunded) {
+ this.lifetimeDollarsRefunded = lifetimeDollarsRefunded != null ? lifetimeDollarsRefunded.getValue() : null;
+ return this;
+ }
+
+ /**
+ * A value that indicates the total amount, in USD, of refunds the customer has received, in your app, across all platforms.
+ *
+ * @return lifetimeDollarsRefunded
+ * @see lifetimeDollarsRefunded
+ **/
+ public LifetimeDollarsRefunded getLifetimeDollarsRefunded() {
+ return lifetimeDollarsRefunded != null ? LifetimeDollarsRefunded.fromValue(lifetimeDollarsRefunded) : null;
+ }
+
+ /**
+ * @see #getLifetimeDollarsRefunded()
+ */
+ public Integer getRawLifetimeDollarsRefunded() {
+ return lifetimeDollarsRefunded;
+ }
+
+ public void setLifetimeDollarsRefunded(LifetimeDollarsRefunded lifetimeDollarsRefunded) {
+ this.lifetimeDollarsRefunded = lifetimeDollarsRefunded != null ? lifetimeDollarsRefunded.getValue() : null;
+ }
+
+ public void setRawLifetimeDollarsRefunded(Integer rawLifetimeDollarsRefunded) {
+ this.lifetimeDollarsRefunded = rawLifetimeDollarsRefunded;
+ }
+
+ public ConsumptionRequestV1 lifetimeDollarsPurchased(LifetimeDollarsPurchased lifetimeDollarsPurchased) {
+ this.lifetimeDollarsPurchased = lifetimeDollarsPurchased != null ? lifetimeDollarsPurchased.getValue() : null;
+ return this;
+ }
+
+ /**
+ * A value that indicates the total amount, in USD, of in-app purchases the customer has made in your app, across all platforms.
+ *
+ * @return lifetimeDollarsPurchased
+ * @see lifetimeDollarsPurchased
+ **/
+ public LifetimeDollarsPurchased getLifetimeDollarsPurchased() {
+ return lifetimeDollarsPurchased != null ? LifetimeDollarsPurchased.fromValue(lifetimeDollarsPurchased) : null;
+ }
+
+ /**
+ * @see #getLifetimeDollarsPurchased()
+ */
+ public Integer getRawLifetimeDollarsPurchased() {
+ return lifetimeDollarsPurchased;
+ }
+
+ public void setLifetimeDollarsPurchased(LifetimeDollarsPurchased lifetimeDollarsPurchased) {
+ this.lifetimeDollarsPurchased = lifetimeDollarsPurchased != null ? lifetimeDollarsPurchased.getValue() : null;
+ }
+
+ public void setRawLifetimeDollarsPurchased(Integer rawLifetimeDollarsPurchased) {
+ this.lifetimeDollarsPurchased = rawLifetimeDollarsPurchased;
+ }
+
+ public ConsumptionRequestV1 userStatus(UserStatus userStatus) {
+ this.userStatus = userStatus != null ? userStatus.getValue() : null;
+ return this;
+ }
+
+ /**
+ * The status of the customer’s account.
+ *
+ * @return userStatus
+ * @see userStatus
+ **/
+ public UserStatus getUserStatus() {
+ return userStatus != null ? UserStatus.fromValue(userStatus) : null;
+ }
+
+ /**
+ * @see #getUserStatus()
+ */
+ public Integer getRawUserStatus() {
+ return userStatus;
+ }
+
+ public void setUserStatus(UserStatus userStatus) {
+ this.userStatus = userStatus != null ? userStatus.getValue() : null;
+ }
+
+ public void setRawUserStatus(Integer rawUserStatus) {
+ this.userStatus = rawUserStatus;
+ }
+
+ public ConsumptionRequestV1 refundPreference(RefundPreferenceV1 refundPreference) {
+ this.refundPreference = refundPreference != null ? refundPreference.getValue() : null;
+ return this;
+ }
+
+ /**
+ * A value that indicates your preference, based on your operational logic, as to whether Apple should grant the refund.
+ *
+ * @return refundPreference
+ * @see refundPreferenceV1
+ **/
+ public RefundPreferenceV1 getRefundPreference() {
+ return refundPreference != null ? RefundPreferenceV1.fromValue(refundPreference) : null;
+ }
+
+ /**
+ * @see #getRefundPreference()
+ */
+ public Integer getRawRefundPreference() {
+ return refundPreference;
+ }
+
+ public void setRefundPreference(RefundPreferenceV1 refundPreference) {
+ this.refundPreference = refundPreference != null ? refundPreference.getValue() : null;
+ }
+
+ public void setRawRefundPreference(Integer rawRefundPreference) {
+ this.refundPreference = rawRefundPreference;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ ConsumptionRequestV1 consumptionRequest = (ConsumptionRequestV1) o;
+ return Objects.equals(this.customerConsented, consumptionRequest.customerConsented) &&
+ Objects.equals(this.consumptionStatus, consumptionRequest.consumptionStatus) &&
+ Objects.equals(this.platform, consumptionRequest.platform) &&
+ Objects.equals(this.sampleContentProvided, consumptionRequest.sampleContentProvided) &&
+ Objects.equals(this.deliveryStatus, consumptionRequest.deliveryStatus) &&
+ Objects.equals(this.appAccountToken, consumptionRequest.appAccountToken) &&
+ Objects.equals(this.accountTenure, consumptionRequest.accountTenure) &&
+ Objects.equals(this.playTime, consumptionRequest.playTime) &&
+ Objects.equals(this.lifetimeDollarsRefunded, consumptionRequest.lifetimeDollarsRefunded) &&
+ Objects.equals(this.lifetimeDollarsPurchased, consumptionRequest.lifetimeDollarsPurchased) &&
+ Objects.equals(this.userStatus, consumptionRequest.userStatus) &&
+ Objects.equals(this.refundPreference, consumptionRequest.refundPreference);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(customerConsented, consumptionStatus, platform, sampleContentProvided, deliveryStatus, appAccountToken, accountTenure, playTime, lifetimeDollarsRefunded, lifetimeDollarsPurchased, userStatus, refundPreference);
+ }
+
+ @Override
+ public String toString() {
+ return "ConsumptionRequestV1{" +
+ "customerConsented=" + customerConsented +
+ ", consumptionStatus=" + consumptionStatus +
+ ", platform=" + platform +
+ ", sampleContentProvided=" + sampleContentProvided +
+ ", deliveryStatus=" + deliveryStatus +
+ ", appAccountToken=" + appAccountToken +
+ ", accountTenure=" + accountTenure +
+ ", playTime=" + playTime +
+ ", lifetimeDollarsRefunded=" + lifetimeDollarsRefunded +
+ ", lifetimeDollarsPurchased=" + lifetimeDollarsPurchased +
+ ", userStatus=" + userStatus +
+ ", refundPreference=" + refundPreference +
+ '}';
+ }
+}
diff --git a/src/main/java/com/apple/itunes/storekit/model/DeliveryStatus.java b/src/main/java/com/apple/itunes/storekit/model/DeliveryStatus.java
index 4b236068..daa71f99 100644
--- a/src/main/java/com/apple/itunes/storekit/model/DeliveryStatus.java
+++ b/src/main/java/com/apple/itunes/storekit/model/DeliveryStatus.java
@@ -1,30 +1,29 @@
-// Copyright (c) 2023 Apple Inc. Licensed under MIT License.
+// Copyright (c) 2025 Apple Inc. Licensed under MIT License.
package com.apple.itunes.storekit.model;
import com.fasterxml.jackson.annotation.JsonValue;
/**
- * A value that indicates whether the app successfully delivered an in-app purchase that works properly.
+ * A value that indicates whether the app successfully delivered an In-App Purchase that works properly.
*
* @see deliveryStatus
*/
public enum DeliveryStatus {
- DELIVERED_AND_WORKING_PROPERLY(0),
- DID_NOT_DELIVER_DUE_TO_QUALITY_ISSUE(1),
- DELIVERED_WRONG_ITEM(2),
- DID_NOT_DELIVER_DUE_TO_SERVER_OUTAGE(3),
- DID_NOT_DELIVER_DUE_TO_IN_GAME_CURRENCY_CHANGE(4),
- DID_NOT_DELIVER_FOR_OTHER_REASON(5);
+ DELIVERED("DELIVERED"),
+ UNDELIVERED_QUALITY_ISSUE("UNDELIVERED_QUALITY_ISSUE"),
+ UNDELIVERED_WRONG_ITEM("UNDELIVERED_WRONG_ITEM"),
+ UNDELIVERED_SERVER_OUTAGE("UNDELIVERED_SERVER_OUTAGE"),
+ UNDELIVERED_OTHER("UNDELIVERED_OTHER");
- private final Integer value;
+ private final String value;
- DeliveryStatus(Integer value) {
+ DeliveryStatus(String value) {
this.value = value;
}
- public static DeliveryStatus fromValue(Integer value) {
+ public static DeliveryStatus fromValue(String value) {
for (DeliveryStatus b : DeliveryStatus.values()) {
if (b.value.equals(value)) {
return b;
@@ -34,7 +33,7 @@ public static DeliveryStatus fromValue(Integer value) {
}
@JsonValue
- public Integer getValue() {
+ public String getValue() {
return value;
}
@@ -43,4 +42,3 @@ public String toString() {
return String.valueOf(value);
}
}
-
diff --git a/src/main/java/com/apple/itunes/storekit/model/DeliveryStatusV1.java b/src/main/java/com/apple/itunes/storekit/model/DeliveryStatusV1.java
new file mode 100644
index 00000000..3a103969
--- /dev/null
+++ b/src/main/java/com/apple/itunes/storekit/model/DeliveryStatusV1.java
@@ -0,0 +1,47 @@
+// Copyright (c) 2023 Apple Inc. Licensed under MIT License.
+
+package com.apple.itunes.storekit.model;
+
+import com.fasterxml.jackson.annotation.JsonValue;
+
+/**
+ * A value that indicates whether the app successfully delivered an in-app purchase that works properly.
+ *
+ * @see deliveryStatus
+ * @deprecated Use {@link DeliveryStatus} instead.
+ */
+@Deprecated
+public enum DeliveryStatusV1 {
+
+ DELIVERED_AND_WORKING_PROPERLY(0),
+ DID_NOT_DELIVER_DUE_TO_QUALITY_ISSUE(1),
+ DELIVERED_WRONG_ITEM(2),
+ DID_NOT_DELIVER_DUE_TO_SERVER_OUTAGE(3),
+ DID_NOT_DELIVER_DUE_TO_IN_GAME_CURRENCY_CHANGE(4),
+ DID_NOT_DELIVER_FOR_OTHER_REASON(5);
+
+ private final Integer value;
+
+ DeliveryStatusV1(Integer value) {
+ this.value = value;
+ }
+
+ public static DeliveryStatusV1 fromValue(Integer value) {
+ for (DeliveryStatusV1 b : DeliveryStatusV1.values()) {
+ if (b.value.equals(value)) {
+ return b;
+ }
+ }
+ return null;
+ }
+
+ @JsonValue
+ public Integer getValue() {
+ return value;
+ }
+
+ @Override
+ public String toString() {
+ return String.valueOf(value);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/apple/itunes/storekit/model/JWSTransactionDecodedPayload.java b/src/main/java/com/apple/itunes/storekit/model/JWSTransactionDecodedPayload.java
index 14f517e9..21d4922f 100644
--- a/src/main/java/com/apple/itunes/storekit/model/JWSTransactionDecodedPayload.java
+++ b/src/main/java/com/apple/itunes/storekit/model/JWSTransactionDecodedPayload.java
@@ -44,6 +44,8 @@ public class JWSTransactionDecodedPayload implements DecodedSignedData {
private static final String SERIALIZED_NAME_OFFER_DISCOUNT_TYPE = "offerDiscountType";
private static final String SERIALIZED_NAME_APP_TRANSACTION_ID = "appTransactionId";
private static final String SERIALIZED_NAME_OFFER_PERIOD = "offerPeriod";
+ private static final String SERIALIZED_NAME_REVOCATION_TYPE = "revocationType";
+ private static final String SERIALIZED_NAME_REVOCATION_PERCENTAGE = "revocationPercentage";
@JsonProperty(SERIALIZED_NAME_ORIGINAL_TRANSACTION_ID)
private String originalTransactionId;
@JsonProperty(SERIALIZED_NAME_TRANSACTION_ID)
@@ -105,6 +107,10 @@ public class JWSTransactionDecodedPayload implements DecodedSignedData {
private String appTransactionId;
@JsonProperty(SERIALIZED_NAME_OFFER_PERIOD)
private String offerPeriod;
+ @JsonProperty(SERIALIZED_NAME_REVOCATION_TYPE)
+ private String revocationType;
+ @JsonProperty(SERIALIZED_NAME_REVOCATION_PERCENTAGE)
+ private Integer revocationPercentage;
@JsonAnySetter
private Map unknownFields;
@@ -721,6 +727,54 @@ public void setOfferPeriod(String offerPeriod) {
this.offerPeriod = offerPeriod;
}
+ public JWSTransactionDecodedPayload revocationType(RevocationType revocationType) {
+ this.revocationType = revocationType != null ? revocationType.getValue() : null;
+ return this;
+ }
+
+ /**
+ * The type of the refund or revocation that applies to the transaction.
+ *
+ * @return revocationType
+ * @see revocationType
+ **/
+ public RevocationType getRevocationType() {
+ return revocationType != null ? RevocationType.fromValue(revocationType) : null;
+ }
+
+ /**
+ * @see #getRevocationType()
+ */
+ public String getRawRevocationType() {
+ return revocationType;
+ }
+
+ public void setRevocationType(RevocationType revocationType) {
+ this.revocationType = revocationType != null ? revocationType.getValue() : null;
+ }
+
+ public void setRawRevocationType(String rawRevocationType) {
+ this.revocationType = rawRevocationType;
+ }
+
+ public JWSTransactionDecodedPayload revocationPercentage(Integer revocationPercentage) {
+ this.revocationPercentage = revocationPercentage;
+ return this;
+ }
+
+ /**
+ * The percentage, in milliunits, of the transaction that the App Store has refunded or revoked.
+ *
+ * @return revocationPercentage
+ * @see revocationPercentage
+ **/
+ public Integer getRevocationPercentage() {
+ return revocationPercentage;
+ }
+
+ public void setRevocationPercentage(Integer revocationPercentage) {
+ this.revocationPercentage = revocationPercentage;
+ }
public JWSTransactionDecodedPayload unknownFields(Map unknownFields) {
this.unknownFields = unknownFields;
@@ -745,12 +799,12 @@ public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
JWSTransactionDecodedPayload that = (JWSTransactionDecodedPayload) o;
- return Objects.equals(originalTransactionId, that.originalTransactionId) && Objects.equals(transactionId, that.transactionId) && Objects.equals(webOrderLineItemId, that.webOrderLineItemId) && Objects.equals(bundleId, that.bundleId) && Objects.equals(productId, that.productId) && Objects.equals(subscriptionGroupIdentifier, that.subscriptionGroupIdentifier) && Objects.equals(purchaseDate, that.purchaseDate) && Objects.equals(originalPurchaseDate, that.originalPurchaseDate) && Objects.equals(expiresDate, that.expiresDate) && Objects.equals(quantity, that.quantity) && Objects.equals(type, that.type) && Objects.equals(appAccountToken, that.appAccountToken) && Objects.equals(inAppOwnershipType, that.inAppOwnershipType) && Objects.equals(signedDate, that.signedDate) && Objects.equals(revocationReason, that.revocationReason) && Objects.equals(revocationDate, that.revocationDate) && Objects.equals(isUpgraded, that.isUpgraded) && Objects.equals(offerType, that.offerType) && Objects.equals(offerIdentifier, that.offerIdentifier) && Objects.equals(environment, that.environment) && Objects.equals(storefront, that.storefront) && Objects.equals(storefrontId, that.storefrontId) && Objects.equals(transactionReason, that.transactionReason) && Objects.equals(price, that.price) && Objects.equals(currency, that.currency) && Objects.equals(offerDiscountType, that.offerDiscountType) && Objects.equals(appTransactionId, that.appTransactionId) && Objects.equals(offerPeriod, that.offerPeriod) && Objects.equals(unknownFields, that.unknownFields);
+ return Objects.equals(originalTransactionId, that.originalTransactionId) && Objects.equals(transactionId, that.transactionId) && Objects.equals(webOrderLineItemId, that.webOrderLineItemId) && Objects.equals(bundleId, that.bundleId) && Objects.equals(productId, that.productId) && Objects.equals(subscriptionGroupIdentifier, that.subscriptionGroupIdentifier) && Objects.equals(purchaseDate, that.purchaseDate) && Objects.equals(originalPurchaseDate, that.originalPurchaseDate) && Objects.equals(expiresDate, that.expiresDate) && Objects.equals(quantity, that.quantity) && Objects.equals(type, that.type) && Objects.equals(appAccountToken, that.appAccountToken) && Objects.equals(inAppOwnershipType, that.inAppOwnershipType) && Objects.equals(signedDate, that.signedDate) && Objects.equals(revocationReason, that.revocationReason) && Objects.equals(revocationDate, that.revocationDate) && Objects.equals(isUpgraded, that.isUpgraded) && Objects.equals(offerType, that.offerType) && Objects.equals(offerIdentifier, that.offerIdentifier) && Objects.equals(environment, that.environment) && Objects.equals(storefront, that.storefront) && Objects.equals(storefrontId, that.storefrontId) && Objects.equals(transactionReason, that.transactionReason) && Objects.equals(price, that.price) && Objects.equals(currency, that.currency) && Objects.equals(offerDiscountType, that.offerDiscountType) && Objects.equals(appTransactionId, that.appTransactionId) && Objects.equals(offerPeriod, that.offerPeriod) && Objects.equals(revocationType, that.revocationType) && Objects.equals(revocationPercentage, that.revocationPercentage) && Objects.equals(unknownFields, that.unknownFields);
}
@Override
public int hashCode() {
- return Objects.hash(originalTransactionId, transactionId, webOrderLineItemId, bundleId, productId, subscriptionGroupIdentifier, purchaseDate, originalPurchaseDate, expiresDate, quantity, type, appAccountToken, inAppOwnershipType, signedDate, revocationReason, revocationDate, isUpgraded, offerType, offerIdentifier, environment, storefront, storefrontId, transactionReason, price, currency, offerDiscountType, appTransactionId, offerPeriod, unknownFields);
+ return Objects.hash(originalTransactionId, transactionId, webOrderLineItemId, bundleId, productId, subscriptionGroupIdentifier, purchaseDate, originalPurchaseDate, expiresDate, quantity, type, appAccountToken, inAppOwnershipType, signedDate, revocationReason, revocationDate, isUpgraded, offerType, offerIdentifier, environment, storefront, storefrontId, transactionReason, price, currency, offerDiscountType, appTransactionId, offerPeriod, revocationType, revocationPercentage, unknownFields);
}
@Override
@@ -784,6 +838,8 @@ public String toString() {
", offerDiscountType='" + offerDiscountType + '\'' +
", appTransactionId='" + appTransactionId + '\'' +
", offerPeriod='" + offerPeriod + '\'' +
+ ", revocationType='" + revocationType + '\'' +
+ ", revocationPercentage=" + revocationPercentage +
", unknownFields=" + unknownFields +
'}';
}
diff --git a/src/main/java/com/apple/itunes/storekit/model/NotificationTypeV2.java b/src/main/java/com/apple/itunes/storekit/model/NotificationTypeV2.java
index 92255c17..874e42ce 100644
--- a/src/main/java/com/apple/itunes/storekit/model/NotificationTypeV2.java
+++ b/src/main/java/com/apple/itunes/storekit/model/NotificationTypeV2.java
@@ -29,7 +29,8 @@ public enum NotificationTypeV2 {
RENEWAL_EXTENSION("RENEWAL_EXTENSION"),
REFUND_REVERSED("REFUND_REVERSED"),
EXTERNAL_PURCHASE_TOKEN("EXTERNAL_PURCHASE_TOKEN"),
- ONE_TIME_CHARGE("ONE_TIME_CHARGE");
+ ONE_TIME_CHARGE("ONE_TIME_CHARGE"),
+ RESCIND_CONSENT("RESCIND_CONSENT");
private final String value;
diff --git a/src/main/java/com/apple/itunes/storekit/model/RefundPreference.java b/src/main/java/com/apple/itunes/storekit/model/RefundPreference.java
index 1aaa5907..714e2fb5 100644
--- a/src/main/java/com/apple/itunes/storekit/model/RefundPreference.java
+++ b/src/main/java/com/apple/itunes/storekit/model/RefundPreference.java
@@ -1,4 +1,4 @@
-// Copyright (c) 2024 Apple Inc. Licensed under MIT License.
+// Copyright (c) 2025 Apple Inc. Licensed under MIT License.
package com.apple.itunes.storekit.model;
@@ -11,18 +11,17 @@
*/
public enum RefundPreference {
- UNDECLARED(0),
- PREFER_GRANT(1),
- PREFER_DECLINE(2),
- NO_PREFERENCE(3);
+ DECLINE("DECLINE"),
+ GRANT_FULL("GRANT_FULL"),
+ GRANT_PRORATED("GRANT_PRORATED");
- private final Integer value;
+ private final String value;
- RefundPreference(Integer value) {
+ RefundPreference(String value) {
this.value = value;
}
- public static RefundPreference fromValue(Integer value) {
+ public static RefundPreference fromValue(String value) {
for (RefundPreference b : RefundPreference.values()) {
if (b.value.equals(value)) {
return b;
@@ -32,7 +31,7 @@ public static RefundPreference fromValue(Integer value) {
}
@JsonValue
- public Integer getValue() {
+ public String getValue() {
return value;
}
@@ -40,5 +39,4 @@ public Integer getValue() {
public String toString() {
return String.valueOf(value);
}
-}
-
+}
\ No newline at end of file
diff --git a/src/main/java/com/apple/itunes/storekit/model/RefundPreferenceV1.java b/src/main/java/com/apple/itunes/storekit/model/RefundPreferenceV1.java
new file mode 100644
index 00000000..f248184f
--- /dev/null
+++ b/src/main/java/com/apple/itunes/storekit/model/RefundPreferenceV1.java
@@ -0,0 +1,46 @@
+// Copyright (c) 2024 Apple Inc. Licensed under MIT License.
+
+package com.apple.itunes.storekit.model;
+
+import com.fasterxml.jackson.annotation.JsonValue;
+
+/**
+ * A value that indicates your preferred outcome for the refund request.
+ *
+ * @see refundPreferenceV1
+ * @deprecated Use {@link RefundPreference} instead.
+ */
+@Deprecated
+public enum RefundPreferenceV1 {
+
+ UNDECLARED(0),
+ PREFER_GRANT(1),
+ PREFER_DECLINE(2),
+ NO_PREFERENCE(3);
+
+ private final Integer value;
+
+ RefundPreferenceV1(Integer value) {
+ this.value = value;
+ }
+
+ public static RefundPreferenceV1 fromValue(Integer value) {
+ for (RefundPreferenceV1 b : RefundPreferenceV1.values()) {
+ if (b.value.equals(value)) {
+ return b;
+ }
+ }
+ return null;
+ }
+
+ @JsonValue
+ public Integer getValue() {
+ return value;
+ }
+
+ @Override
+ public String toString() {
+ return String.valueOf(value);
+ }
+}
+
diff --git a/src/main/java/com/apple/itunes/storekit/model/ResponseBodyV2DecodedPayload.java b/src/main/java/com/apple/itunes/storekit/model/ResponseBodyV2DecodedPayload.java
index 2b5b636e..00f2bc05 100644
--- a/src/main/java/com/apple/itunes/storekit/model/ResponseBodyV2DecodedPayload.java
+++ b/src/main/java/com/apple/itunes/storekit/model/ResponseBodyV2DecodedPayload.java
@@ -22,6 +22,7 @@ public class ResponseBodyV2DecodedPayload implements DecodedSignedData {
private static final String SERIALIZED_NAME_SIGNED_DATE = "signedDate";
private static final String SERIALIZED_NAME_SUMMARY = "summary";
private static final String SERIALIZED_NAME_EXTERNAL_PURCHASE_TOKEN = "externalPurchaseToken";
+ private static final String SERIALIZED_NAME_APP_DATA = "appData";
@JsonProperty(SERIALIZED_NAME_NOTIFICATION_TYPE)
private String notificationType;
@JsonProperty(SERIALIZED_NAME_SUBTYPE)
@@ -38,6 +39,8 @@ public class ResponseBodyV2DecodedPayload implements DecodedSignedData {
private Summary summary;
@JsonProperty(SERIALIZED_NAME_EXTERNAL_PURCHASE_TOKEN)
private ExternalPurchaseToken externalPurchaseToken;
+ @JsonProperty(SERIALIZED_NAME_APP_DATA)
+ private AppData appData;
@JsonAnySetter
private Map unknownFields;
@@ -222,6 +225,25 @@ public void setExternalPurchaseToken(ExternalPurchaseToken externalPurchaseToken
this.externalPurchaseToken = externalPurchaseToken;
}
+ public ResponseBodyV2DecodedPayload appData(AppData appData) {
+ this.appData = appData;
+ return this;
+ }
+
+ /**
+ * The object that contains the app metadata and signed app transaction information. This field appears when the notificationType is RESCIND_CONSENT.
+ *
+ * @return appData
+ * @see appData
+ **/
+ public AppData getAppData() {
+ return appData;
+ }
+
+ public void setAppData(AppData appData) {
+ this.appData = appData;
+ }
+
public ResponseBodyV2DecodedPayload unknownFields(Map unknownFields) {
this.unknownFields = unknownFields;
return this;
@@ -257,12 +279,13 @@ public boolean equals(Object o) {
Objects.equals(this.signedDate, responseBodyV2DecodedPayload.signedDate) &&
Objects.equals(this.summary, responseBodyV2DecodedPayload.summary) &&
Objects.equals(this.externalPurchaseToken, responseBodyV2DecodedPayload.externalPurchaseToken) &&
+ Objects.equals(this.appData, responseBodyV2DecodedPayload.appData) &&
Objects.equals(this.unknownFields, responseBodyV2DecodedPayload.unknownFields);
}
@Override
public int hashCode() {
- return Objects.hash(notificationType, subtype, notificationUUID, data, version, signedDate, summary, externalPurchaseToken, unknownFields);
+ return Objects.hash(notificationType, subtype, notificationUUID, data, version, signedDate, summary, externalPurchaseToken, appData, unknownFields);
}
@Override
@@ -276,6 +299,7 @@ public String toString() {
", signedDate=" + signedDate +
", summary=" + summary +
", externalPurchaseToken=" + externalPurchaseToken +
+ ", appData=" + appData +
", unknownFields=" + unknownFields +
'}';
}
diff --git a/src/main/java/com/apple/itunes/storekit/model/RevocationType.java b/src/main/java/com/apple/itunes/storekit/model/RevocationType.java
new file mode 100644
index 00000000..978509db
--- /dev/null
+++ b/src/main/java/com/apple/itunes/storekit/model/RevocationType.java
@@ -0,0 +1,42 @@
+// Copyright (c) 2025 Apple Inc. Licensed under MIT License.
+
+package com.apple.itunes.storekit.model;
+
+import com.fasterxml.jackson.annotation.JsonValue;
+
+/**
+ * The type of the refund or revocation that applies to the transaction.
+ *
+ * @see revocationType
+ */
+public enum RevocationType {
+
+ REFUND_FULL("REFUND_FULL"),
+ REFUND_PRORATED("REFUND_PRORATED"),
+ FAMILY_REVOKE("FAMILY_REVOKE");
+
+ private final String value;
+
+ RevocationType(String value) {
+ this.value = value;
+ }
+
+ public static RevocationType fromValue(String value) {
+ for (RevocationType b : RevocationType.values()) {
+ if (b.value.equals(value)) {
+ return b;
+ }
+ }
+ return null;
+ }
+
+ @JsonValue
+ public String getValue() {
+ return value;
+ }
+
+ @Override
+ public String toString() {
+ return String.valueOf(value);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/apple/itunes/storekit/verification/SignedDataVerifier.java b/src/main/java/com/apple/itunes/storekit/verification/SignedDataVerifier.java
index 34949535..33601d9f 100644
--- a/src/main/java/com/apple/itunes/storekit/verification/SignedDataVerifier.java
+++ b/src/main/java/com/apple/itunes/storekit/verification/SignedDataVerifier.java
@@ -120,6 +120,10 @@ public ResponseBodyV2DecodedPayload verifyAndDecodeNotification(String signedPay
} else {
notificationEnv = Environment.PRODUCTION;
}
+ } else if (notification.getAppData() != null) {
+ bundleId = notification.getAppData().getBundleId();
+ appAppleId = notification.getAppData().getAppAppleId();
+ notificationEnv = notification.getAppData().getEnvironment();
} else {
bundleId = null;
appAppleId = null;
diff --git a/src/test/java/com/apple/itunes/storekit/client/AppStoreServerAPIClientTest.java b/src/test/java/com/apple/itunes/storekit/client/AppStoreServerAPIClientTest.java
index ffe55ace..4f37185a 100644
--- a/src/test/java/com/apple/itunes/storekit/client/AppStoreServerAPIClientTest.java
+++ b/src/test/java/com/apple/itunes/storekit/client/AppStoreServerAPIClientTest.java
@@ -6,9 +6,11 @@
import com.apple.itunes.storekit.model.AppTransactionInfoResponse;
import com.apple.itunes.storekit.model.CheckTestNotificationResponse;
import com.apple.itunes.storekit.model.ConsumptionRequest;
+import com.apple.itunes.storekit.model.ConsumptionRequestV1;
import com.apple.itunes.storekit.model.ConsumptionStatus;
import com.apple.itunes.storekit.model.DefaultConfigurationRequest;
import com.apple.itunes.storekit.model.DeliveryStatus;
+import com.apple.itunes.storekit.model.DeliveryStatusV1;
import com.apple.itunes.storekit.model.Environment;
import com.apple.itunes.storekit.model.ExtendReasonCode;
import com.apple.itunes.storekit.model.ExtendRenewalDateRequest;
@@ -35,6 +37,7 @@
import com.apple.itunes.storekit.model.PlayTime;
import com.apple.itunes.storekit.model.RefundHistoryResponse;
import com.apple.itunes.storekit.model.RefundPreference;
+import com.apple.itunes.storekit.model.RefundPreferenceV1;
import com.apple.itunes.storekit.model.SendAttemptItem;
import com.apple.itunes.storekit.model.SendAttemptResult;
import com.apple.itunes.storekit.model.SendTestNotificationResponse;
@@ -481,19 +484,19 @@ public void testSendConsumptionData() throws APIException, IOException {
Assertions.assertEquals(3, ((Number) root.get("refundPreference")).intValue());
});
- ConsumptionRequest consumptionRequest = new ConsumptionRequest()
+ ConsumptionRequestV1 consumptionRequest = new ConsumptionRequestV1()
.customerConsented(true)
.consumptionStatus(ConsumptionStatus.NOT_CONSUMED)
.platform(Platform.NON_APPLE)
.sampleContentProvided(false)
- .deliveryStatus(DeliveryStatus.DID_NOT_DELIVER_DUE_TO_SERVER_OUTAGE)
+ .deliveryStatus(DeliveryStatusV1.DID_NOT_DELIVER_DUE_TO_SERVER_OUTAGE)
.appAccountToken(UUID.fromString("7389a31a-fb6d-4569-a2a6-db7d85d84813"))
.accountTenure(AccountTenure.THIRTY_DAYS_TO_NINETY_DAYS)
.playTime(PlayTime.ONE_DAY_TO_FOUR_DAYS)
.lifetimeDollarsRefunded(LifetimeDollarsRefunded.ONE_THOUSAND_DOLLARS_TO_ONE_THOUSAND_NINE_HUNDRED_NINETY_NINE_DOLLARS_AND_NINETY_NINE_CENTS)
.lifetimeDollarsPurchased(LifetimeDollarsPurchased.TWO_THOUSAND_DOLLARS_OR_GREATER)
.userStatus(UserStatus.LIMITED_ACCESS)
- .refundPreference(RefundPreference.NO_PREFERENCE);
+ .refundPreference(RefundPreferenceV1.NO_PREFERENCE);
client.sendConsumptionData("49571273", consumptionRequest);
}
@@ -532,18 +535,18 @@ public void testSendConsumptionDataWithNullAppAccountToken() throws APIException
Assertions.assertEquals(3, ((Number) root.get("refundPreference")).intValue());
});
- ConsumptionRequest consumptionRequest = new ConsumptionRequest()
+ ConsumptionRequestV1 consumptionRequest = new ConsumptionRequestV1()
.customerConsented(true)
.consumptionStatus(ConsumptionStatus.NOT_CONSUMED)
.platform(Platform.NON_APPLE)
.sampleContentProvided(false)
- .deliveryStatus(DeliveryStatus.DID_NOT_DELIVER_DUE_TO_SERVER_OUTAGE)
+ .deliveryStatus(DeliveryStatusV1.DID_NOT_DELIVER_DUE_TO_SERVER_OUTAGE)
.accountTenure(AccountTenure.THIRTY_DAYS_TO_NINETY_DAYS)
.playTime(PlayTime.ONE_DAY_TO_FOUR_DAYS)
.lifetimeDollarsRefunded(LifetimeDollarsRefunded.ONE_THOUSAND_DOLLARS_TO_ONE_THOUSAND_NINE_HUNDRED_NINETY_NINE_DOLLARS_AND_NINETY_NINE_CENTS)
.lifetimeDollarsPurchased(LifetimeDollarsPurchased.TWO_THOUSAND_DOLLARS_OR_GREATER)
.userStatus(UserStatus.LIMITED_ACCESS)
- .refundPreference(RefundPreference.NO_PREFERENCE);
+ .refundPreference(RefundPreferenceV1.NO_PREFERENCE);
client.sendConsumptionData("49571273", consumptionRequest);
}
@@ -976,7 +979,74 @@ public void testGetAppTransactionInfoTransactionIdNotFoundError() throws IOExcep
Assertions.fail();
}
- public AppStoreServerAPIClient getClientWithBody(String path, Consumer requestVerifier) throws IOException {
+ @Test
+ public void testSendConsumptionInformation() throws APIException, IOException {
+ AppStoreServerAPIClient client = getAppStoreServerAPIClient("", request -> {
+ Assertions.assertEquals("PUT", request.method());
+ Assertions.assertEquals("/inApps/v2/transactions/consumption/49571273", request.url().encodedPath());
+ RequestBody body = request.body();
+ Assertions.assertNotNull(body);
+ Assertions.assertEquals(expectedMediaType, body.contentType());
+ Buffer buffer = new Buffer();
+ try {
+ body.writeTo(buffer);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ Map root;
+ try {
+ root = new ObjectMapper().readValue(buffer.readUtf8(), Map.class);
+ } catch (JsonProcessingException e) {
+ throw new RuntimeException(e);
+ }
+ Assertions.assertTrue((Boolean) root.get("customerConsented"));
+ Assertions.assertEquals(50000, ((Number) root.get("consumptionPercentage")).intValue());
+ Assertions.assertEquals("DELIVERED", root.get("deliveryStatus"));
+ Assertions.assertEquals("GRANT_FULL", root.get("refundPreference"));
+ Assertions.assertFalse((Boolean) root.get("sampleContentProvided"));
+ });
+
+ ConsumptionRequest consumptionRequest = new ConsumptionRequest(true, DeliveryStatus.DELIVERED, false)
+ .consumptionPercentage(50000) // 50% in milliunits
+ .refundPreference(RefundPreference.GRANT_FULL);
+
+ client.sendConsumptionInformation("49571273", consumptionRequest);
+ }
+
+ @Test
+ public void testSendConsumptionInformationWithoutOptionalFields() throws APIException, IOException {
+ AppStoreServerAPIClient client = getAppStoreServerAPIClient("", request -> {
+ Assertions.assertEquals("PUT", request.method());
+ Assertions.assertEquals("/inApps/v2/transactions/consumption/49571273", request.url().encodedPath());
+ RequestBody body = request.body();
+ Assertions.assertNotNull(body);
+ Assertions.assertEquals(expectedMediaType, body.contentType());
+ Buffer buffer = new Buffer();
+ try {
+ body.writeTo(buffer);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ Map root;
+ try {
+ root = new ObjectMapper().readValue(buffer.readUtf8(), Map.class);
+ } catch (JsonProcessingException e) {
+ throw new RuntimeException(e);
+ }
+ Assertions.assertTrue((Boolean) root.get("customerConsented"));
+ Assertions.assertEquals("UNDELIVERED_QUALITY_ISSUE", root.get("deliveryStatus"));
+ Assertions.assertTrue((Boolean) root.get("sampleContentProvided"));
+ // consumptionPercentage and refundPreference should be null/absent
+ Assertions.assertNull(root.get("consumptionPercentage"));
+ Assertions.assertNull(root.get("refundPreference"));
+ });
+
+ ConsumptionRequest consumptionRequest = new ConsumptionRequest(true, DeliveryStatus.UNDELIVERED_QUALITY_ISSUE, true);
+
+ client.sendConsumptionInformation("49571273", consumptionRequest);
+ }
+
+ public AppStoreServerAPIClient getClientWithBody(String path, Consumer requestVerifier) throws IOException {
String body = TestingUtility.readFile(path);
return getAppStoreServerAPIClient(body, requestVerifier);
}
diff --git a/src/test/java/com/apple/itunes/storekit/model/AppDataTest.java b/src/test/java/com/apple/itunes/storekit/model/AppDataTest.java
new file mode 100644
index 00000000..60f0d9e5
--- /dev/null
+++ b/src/test/java/com/apple/itunes/storekit/model/AppDataTest.java
@@ -0,0 +1,28 @@
+// Copyright (c) 2025 Apple Inc. Licensed under MIT License.
+
+package com.apple.itunes.storekit.model;
+
+import com.apple.itunes.storekit.util.TestingUtility;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.io.IOException;
+
+public class AppDataTest {
+
+ private final ObjectMapper objectMapper = new ObjectMapper();
+
+ @Test
+ public void testAppData() throws IOException {
+ String json = TestingUtility.readFile("models/appData.json");
+
+ AppData appData = objectMapper.readValue(json, AppData.class);
+
+ Assertions.assertEquals(Long.valueOf(987654321L), appData.getAppAppleId());
+ Assertions.assertEquals("com.example", appData.getBundleId());
+ Assertions.assertEquals(Environment.SANDBOX, appData.getEnvironment());
+ Assertions.assertEquals("Sandbox", appData.getRawEnvironment());
+ Assertions.assertEquals("signed-app-transaction-info", appData.getSignedAppTransactionInfo());
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/com/apple/itunes/storekit/model/JWSTransactionDecodedPayloadTest.java b/src/test/java/com/apple/itunes/storekit/model/JWSTransactionDecodedPayloadTest.java
index e0202983..1f914ba4 100644
--- a/src/test/java/com/apple/itunes/storekit/model/JWSTransactionDecodedPayloadTest.java
+++ b/src/test/java/com/apple/itunes/storekit/model/JWSTransactionDecodedPayloadTest.java
@@ -56,4 +56,50 @@ public void testTransactionDecoding() throws IOException, NoSuchAlgorithmExcepti
Assertions.assertEquals("71134", transaction.getAppTransactionId());
Assertions.assertEquals("P1Y", transaction.getOfferPeriod());
}
+
+ @Test
+ public void testTransactionWithRevocationDecoding() throws IOException, NoSuchAlgorithmException, VerificationException {
+ String signedTransaction = SignedDataCreator.createSignedDataFromJson("models/signedTransactionWithRevocation.json");
+
+ JWSTransactionDecodedPayload transaction = TestingUtility.getSignedPayloadVerifier().verifyAndDecodeTransaction(signedTransaction);
+
+ Assertions.assertEquals("12345", transaction.getOriginalTransactionId());
+ Assertions.assertEquals("23456", transaction.getTransactionId());
+ Assertions.assertEquals("34343", transaction.getWebOrderLineItemId());
+ Assertions.assertEquals("com.example", transaction.getBundleId());
+ Assertions.assertEquals("com.example.product", transaction.getProductId());
+ Assertions.assertEquals("55555", transaction.getSubscriptionGroupIdentifier());
+ Assertions.assertEquals(1698148800000L, transaction.getOriginalPurchaseDate());
+ Assertions.assertEquals(1698148900000L, transaction.getPurchaseDate());
+ Assertions.assertEquals(1698148950000L, transaction.getRevocationDate());
+ Assertions.assertEquals(1698149000000L, transaction.getExpiresDate());
+ Assertions.assertEquals(1, transaction.getQuantity());
+ Assertions.assertEquals(Type.AUTO_RENEWABLE_SUBSCRIPTION, transaction.getType());
+ Assertions.assertEquals("Auto-Renewable Subscription", transaction.getRawType());
+ Assertions.assertEquals(UUID.fromString("7e3fb20b-4cdb-47cc-936d-99d65f608138"), transaction.getAppAccountToken());
+ Assertions.assertEquals(InAppOwnershipType.PURCHASED, transaction.getInAppOwnershipType());
+ Assertions.assertEquals("PURCHASED", transaction.getRawInAppOwnershipType());
+ Assertions.assertEquals(1698148900000L, transaction.getSignedDate());
+ Assertions.assertEquals(RevocationReason.REFUNDED_DUE_TO_ISSUE, transaction.getRevocationReason());
+ Assertions.assertEquals(1, transaction.getRawRevocationReason());
+ Assertions.assertEquals("abc.123", transaction.getOfferIdentifier());
+ Assertions.assertTrue(transaction.getIsUpgraded());
+ Assertions.assertEquals(OfferType.INTRODUCTORY_OFFER, transaction.getOfferType());
+ Assertions.assertEquals(1, transaction.getRawOfferType());
+ Assertions.assertEquals("USA", transaction.getStorefront());
+ Assertions.assertEquals("143441", transaction.getStorefrontId());
+ Assertions.assertEquals(TransactionReason.PURCHASE, transaction.getTransactionReason());
+ Assertions.assertEquals("PURCHASE", transaction.getRawTransactionReason());
+ Assertions.assertEquals(Environment.LOCAL_TESTING, transaction.getEnvironment());
+ Assertions.assertEquals("LocalTesting", transaction.getRawEnvironment());
+ Assertions.assertEquals(10990, transaction.getPrice());
+ Assertions.assertEquals("USD", transaction.getCurrency());
+ Assertions.assertEquals(OfferDiscountType.PAY_AS_YOU_GO, transaction.getOfferDiscountType());
+ Assertions.assertEquals("PAY_AS_YOU_GO", transaction.getRawOfferDiscountType());
+ Assertions.assertEquals("71134", transaction.getAppTransactionId());
+ Assertions.assertEquals("P1Y", transaction.getOfferPeriod());
+ Assertions.assertEquals(RevocationType.REFUND_PRORATED, transaction.getRevocationType());
+ Assertions.assertEquals("REFUND_PRORATED", transaction.getRawRevocationType());
+ Assertions.assertEquals(50000, transaction.getRevocationPercentage());
+ }
}
diff --git a/src/test/java/com/apple/itunes/storekit/model/ResponseBodyV2DecodedPayloadTest.java b/src/test/java/com/apple/itunes/storekit/model/ResponseBodyV2DecodedPayloadTest.java
index c14bb7f1..10fec909 100644
--- a/src/test/java/com/apple/itunes/storekit/model/ResponseBodyV2DecodedPayloadTest.java
+++ b/src/test/java/com/apple/itunes/storekit/model/ResponseBodyV2DecodedPayloadTest.java
@@ -163,4 +163,28 @@ protected void verifyNotification(String bundleId, Long appAppleId, Environment
Assertions.assertEquals(55555L, notification.getExternalPurchaseToken().getAppAppleId());
Assertions.assertEquals("com.example", notification.getExternalPurchaseToken().getBundleId());
}
+
+ @Test
+ public void testRescindConsentNotificationDecoding() throws IOException, NoSuchAlgorithmException, VerificationException {
+ String signedNotification = SignedDataCreator.createSignedDataFromJson("models/signedRescindConsentNotification.json");
+
+ ResponseBodyV2DecodedPayload notification = TestingUtility.getSignedPayloadVerifier().verifyAndDecodeNotification(signedNotification);
+
+ Assertions.assertEquals(NotificationTypeV2.RESCIND_CONSENT, notification.getNotificationType());
+ Assertions.assertEquals("RESCIND_CONSENT", notification.getRawNotificationType());
+ Assertions.assertNull(notification.getSubtype());
+ Assertions.assertNull(notification.getRawSubtype());
+ Assertions.assertEquals("002e14d5-51f5-4503-b5a8-c3a1af68eb20", notification.getNotificationUUID());
+ Assertions.assertEquals("2.0", notification.getVersion());
+ Assertions.assertEquals(1698148900000L, notification.getSignedDate());
+ Assertions.assertNull(notification.getData());
+ Assertions.assertNull(notification.getSummary());
+ Assertions.assertNull(notification.getExternalPurchaseToken());
+ Assertions.assertNotNull(notification.getAppData());
+ Assertions.assertEquals(Environment.LOCAL_TESTING, notification.getAppData().getEnvironment());
+ Assertions.assertEquals("LocalTesting", notification.getAppData().getRawEnvironment());
+ Assertions.assertEquals(41234L, notification.getAppData().getAppAppleId());
+ Assertions.assertEquals("com.example", notification.getAppData().getBundleId());
+ Assertions.assertEquals("signed_app_transaction_info_value", notification.getAppData().getSignedAppTransactionInfo());
+ }
}
diff --git a/src/test/resources/models/appData.json b/src/test/resources/models/appData.json
new file mode 100644
index 00000000..14b93acb
--- /dev/null
+++ b/src/test/resources/models/appData.json
@@ -0,0 +1,6 @@
+{
+ "appAppleId": 987654321,
+ "bundleId": "com.example",
+ "environment": "Sandbox",
+ "signedAppTransactionInfo": "signed-app-transaction-info"
+}
\ No newline at end of file
diff --git a/src/test/resources/models/signedRescindConsentNotification.json b/src/test/resources/models/signedRescindConsentNotification.json
new file mode 100644
index 00000000..0624e932
--- /dev/null
+++ b/src/test/resources/models/signedRescindConsentNotification.json
@@ -0,0 +1,12 @@
+{
+ "notificationType": "RESCIND_CONSENT",
+ "notificationUUID": "002e14d5-51f5-4503-b5a8-c3a1af68eb20",
+ "appData": {
+ "appAppleId": 41234,
+ "bundleId": "com.example",
+ "environment": "LocalTesting",
+ "signedAppTransactionInfo": "signed_app_transaction_info_value"
+ },
+ "version": "2.0",
+ "signedDate": 1698148900000
+}
\ No newline at end of file
diff --git a/src/test/resources/models/signedTransactionWithRevocation.json b/src/test/resources/models/signedTransactionWithRevocation.json
new file mode 100644
index 00000000..3886b2e1
--- /dev/null
+++ b/src/test/resources/models/signedTransactionWithRevocation.json
@@ -0,0 +1,32 @@
+{
+ "originalTransactionId": "12345",
+ "transactionId": "23456",
+ "webOrderLineItemId": "34343",
+ "bundleId": "com.example",
+ "productId": "com.example.product",
+ "subscriptionGroupIdentifier": "55555",
+ "purchaseDate": 1698148900000,
+ "originalPurchaseDate": 1698148800000,
+ "expiresDate": 1698149000000,
+ "quantity": 1,
+ "type": "Auto-Renewable Subscription",
+ "appAccountToken": "7e3fb20b-4cdb-47cc-936d-99d65f608138",
+ "inAppOwnershipType": "PURCHASED",
+ "signedDate": 1698148900000,
+ "revocationReason": 1,
+ "revocationDate": 1698148950000,
+ "isUpgraded": true,
+ "offerType": 1,
+ "offerIdentifier": "abc.123",
+ "environment": "LocalTesting",
+ "storefront": "USA",
+ "storefrontId": "143441",
+ "transactionReason": "PURCHASE",
+ "price": 10990,
+ "currency": "USD",
+ "offerDiscountType": "PAY_AS_YOU_GO",
+ "appTransactionId": "71134",
+ "offerPeriod": "P1Y",
+ "revocationType": "REFUND_PRORATED",
+ "revocationPercentage": 50000
+}
\ No newline at end of file