Skip to main content

Introduction to entitlement revocation

Last updated on August 13, 2024
info

Extend is in Open Beta for AGS Private Cloud Clients! This means that the Extend add-on is available for you to try in your development environment. You can submit your feedback via our Extend Open Beta feedback form.

Overview

This article walks you through how to modify the Extend Override app template for entitlement revocation and transform it into your own app that fits your requirements.

Contract functions

There is only one function on the contract as shown in the snippet below, which is a unary function called Revoke.

service Revocation {
/**
Revoke
Currently, only Third-Party DLC Refund and Refund Order will trigger this grpc revocation.
*/
rpc Revoke(RevokeRequest) returns (RevokeResponse);
}

Revoke

Revoke allows for the handling of revocation requests triggered by specific events within a gaming or virtual economy system. It is used for implementing the logic to manage entitlements and ensures the proper handling of refunds or revocation orders.

In this example, we will generate some custom responses for the three types of entries that are currently supported: ITEM, ENTITLEMENT, and CURRENCY.

The top-level revoke function gets the revoke entry type from the request and executes the corresponding function.

In the app, the following function can be found in src/master/src/app/services/revocation_service.py.

async def Revoke(self, request: RevokeRequest, context):
self.log_payload(f'{self.Revoke.__name__} request: %s', request)
response : RevokeResponse
try:
namespace = request.namespace
userId = request.userId
quantity = request.quantity
revoke_entry_type = RevokeEntryType[request.revokeEntryType.upper()]
revocation = Revocations().get_revocation(revoke_entry_type)
response = revocation.revoke(namespace, userId, quantity, request)
except Exception as e:
response = RevokeResponse(
reason = f"Revocation method {str(e)} not supported",
status = RevocationStatus.FAIL.name,
)
self.log_payload(f'{self.Revoke.__name__} response: %s', response)
return response

If the revoke entry type is ITEM, the following function is executed.

class ItemRevocation(Revocation):
def __init__(self) -> None:
self.custom_revocation = dict()
super().__init__()

def revoke(self, namespace, userId, quantity, request):

item = request.item
self.custom_revocation["namespace"] = namespace
self.custom_revocation["userId"] = userId
self.custom_revocation["quantity"] = str(quantity)
self.custom_revocation["sku"] = item.itemSku
self.custom_revocation["itemType"] = item.itemType
self.custom_revocation["useCount"] = str(item.useCount)
self.custom_revocation["entitlementType"] = item.entitlementType
return RevokeResponse(
customRevocation = self.custom_revocation,
status = RevocationStatus.SUCCESS.name,
)

If the revoke entry type is CURRENCY, the following function is executed.

class CurrencyRevocation(Revocation):

def __init__(self) -> None:
self.custom_revocation = dict()
super().__init__()

def revoke(self, namespace, userId, quantity, request):
currency = request.currency
self.custom_revocation["namespace"] = namespace
self.custom_revocation["userId"] = userId
self.custom_revocation["quantity"] = str(quantity)
self.custom_revocation["currencyNamespace"] = currency.namespace
self.custom_revocation["currencyCode"] = currency.currencyCode
self.custom_revocation["balanceOrigin"] = currency.balanceOrigin
return RevokeResponse(
customRevocation = self.custom_revocation,
status = RevocationStatus.SUCCESS.name,
)

If the revoke entry type is ENTITLEMENT, the following function is executed.

class EntitlementRevocation(Revocation):
def __init__(self) -> None:
self.custom_revocation = dict()
super().__init__()

def revoke(self, namespace, userId, quantity, request):

entitlement = request.entitlement
self.custom_revocation["namespace"] = namespace
self.custom_revocation["userId"] = userId
self.custom_revocation["quantity"] = str(quantity)
self.custom_revocation["entitlementId"] = entitlement.entitlementId
self.custom_revocation["itemId"] = entitlement.itemId
self.custom_revocation["sku"] = entitlement.sku
return RevokeResponse(
customRevocation = self.custom_revocation,
status = RevocationStatus.SUCCESS.name,
)