エンタイトルメント取り消し入門
注釈:本資料はAI技術を用いて翻訳されています。
概要
この記事では、エンタイトルメント取り消しのための Extend Override アプリで使用される API コントラクト(Protobuf)について説明します。
service Revocation {
/**
Revoke
Currently, only Third-Party DLC Refund and Refund Order will trigger this grpc revocation.
*/
rpc Revoke(RevokeRequest) returns (RevokeResponse);
}
API コントラクト
Revoke
Revoke は、ゲームまたは仮想経済システム内の特定のイベントによってトリガーされる取り消しリクエストの処理を可能にします。エンタイトルメントを管理するロジックを実装し、返金または取り消し注文の適切な処理を保証するために使用されます。
この例では、現在サポートされている3つのエントリタイプ(ITEM、ENTITLEMENT、CURRENCY)に対してカスタムレスポンスを生成します。
トップレベルの revoke 関数は、リクエストから revoke エントリタイプを取得し、対応する関数を実行します。
- C#
- Go
- Java
- Python
In the app, the following function can be found in src/AccelByte.PluginArch.Revocation.Demo.Server/Services/RevocationFunctionService.cs.
public override Task<RevokeResponse> Revoke(RevokeRequest request, ServerCallContext context)
{
var response = new RevokeResponse();
response.Status = "SUCCESS";
string revokeType = request.RevokeEntryType.Trim().ToUpper();
if (revokeType == "ITEM")
{
...
}
else if (revokeType == "CURRENCY")
{
...
}
else if (revokeType == "ENTITLEMENT")
{
...
}
else
{
response.Status = "FAIL";
response.Reason = $"Revocation type [{revokeType}] is not supported.";
}
return Task.FromResult(response);
}
In the app, the following function can be found in pkg/service/revocation_service.go.
func (s *RevocationServiceServer) Revoke(ctx context.Context, req *pb.RevokeRequest) (*pb.RevokeResponse, error) {
revocationEntryType := revocation.RevokeEntryType(req.GetRevokeEntryType())
revocationObj, err := revocation.GetRevocation(revocationEntryType)
if err != nil {
return &pb.RevokeResponse{
Status: revocation.StatusFail,
Reason: err.Error(),
}, nil
}
revocationResp, err := revocationObj.Revoke(req.GetNamespace(), req.GetUserId(), req.GetQuantity(), req)
if err != nil {
return &pb.RevokeResponse{
Status: revocation.StatusFail,
Reason: err.Error(),
}, nil
}
return revocationResp, nil
}
In the app, the following function can be found in src/main/java/net/accelbyte/service/RevocationServiceImplementation.java.
@Override
public void revoke(RevokeRequest request, StreamObserver<RevokeResponse> responseObserver) {
RevokeResponse response;
try {
String namespace = request.getNamespace();
String userId = request.getUserId();
int quantity = request.getQuantity();
RevokeEntryType revokeEntryType = RevokeEntryType.valueOf(request.getRevokeEntryType().toUpperCase());
Revocation revocation = Revocations.getRevocation(revokeEntryType);
response = revocation.revoke(namespace, userId, quantity, request);
} catch (Throwable ex) {
response = RevokeResponse.newBuilder()
.setStatus(RevocationStatus.FAIL.name())
.setReason(ex.getMessage())
.build();
}
responseObserver.onNext(response);
responseObserver.onCompleted();
}
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
revoke エントリタイプが ITEM の場合、以下の関数が実行されます。
- C#
- Go
- Java
- Python
response.CustomRevocation.Add(new Dictionary<string, string>()
{
{ "namespace", request.Namespace },
{ "userId", request.UserId },
{ "quantity", request.Quantity.ToString() },
{ "itemId", request.Item.ItemId },
{ "sku", request.Item.ItemSku },
{ "itemType", request.Item.ItemType },
{ "useCount", request.Item.UseCount.ToString() },
{ "entitlementType", request.Item.EntitlementType }
});
func (r *ItemRevocation) Revoke(namespace string, userId string, quantity int32, request *pb.RevokeRequest) (*pb.RevokeResponse, error) {
item := request.GetItem()
customRevocation := map[string]string{}
customRevocation["namespace"] = namespace
customRevocation["userId"] = userId
customRevocation["quantity"] = fmt.Sprintf("%d", quantity)
customRevocation["itemId"] = item.GetItemId()
customRevocation["sku"] = item.GetItemSku()
customRevocation["itemType"] = item.GetItemType()
customRevocation["useCount"] = fmt.Sprintf("%d", item.GetUseCount())
customRevocation["entitlementType"] = item.GetEntitlementType()
return &pb.RevokeResponse{
Status: StatusSuccess,
CustomRevocation: customRevocation,
}, nil
}
public class ItemRevocation implements Revocation {
@Override
public RevokeResponse revoke(String namespace, String userId, int quantity, RevokeRequest request) {
Map<String, String> customRevocation = new HashMap<>();
// Execute your logic; this is for demo purposes only
RevokeItemObject item = request.getItem();
customRevocation.put("namespace", namespace);
customRevocation.put("userId", userId);
customRevocation.put("quantity", String.valueOf(quantity));
customRevocation.put("itemId", item.getItemId());
customRevocation.put("sku", item.getItemSku());
customRevocation.put("itemType", item.getItemType());
customRevocation.put("useCount", String.valueOf(item.getUseCount()));
customRevocation.put("entitlementType", item.getEntitlementType());
return RevokeResponse.newBuilder()
.putAllCustomRevocation(customRevocation)
.setStatus(RevocationStatus.SUCCESS.name()).build();
}
}
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,
)
revoke エントリタイプが CURRENCY の場合、以下の関数が実行されます。
- C#
- Go
- Java
- Python
response.CustomRevocation.Add(new Dictionary<string, string>()
{
{ "namespace", request.Namespace },
{ "userId", request.UserId },
{ "quantity", request.Quantity.ToString() },
{ "currencyNamespace", request.Currency.Namespace },
{ "currencyCode", request.Currency.CurrencyCode },
{ "balanceOrigin", request.Currency.BalanceOrigin }
});
func (r *CurrencyRevocation) Revoke(namespace string, userId string, quantity int32, request *pb.RevokeRequest) (*pb.RevokeResponse, error) {
currency := request.GetCurrency()
customRevocation := map[string]string{}
customRevocation["namespace"] = namespace
customRevocation["userId"] = userId
customRevocation["quantity"] = fmt.Sprintf("%d", quantity)
customRevocation["currencyNamespace"] = currency.GetNamespace()
customRevocation["currencyCode"] = currency.GetCurrencyCode()
customRevocation["balanceOrigin"] = currency.GetBalanceOrigin()
return &pb.RevokeResponse{
Status: StatusSuccess,
CustomRevocation: customRevocation,
}, nil
}
public class CurrencyRevocation implements Revocation {
@Override
public RevokeResponse revoke(String namespace, String userId, int quantity, RevokeRequest request) {
Map<String, String> customRevocation = new HashMap<>();
// Execute your logic; this is for demo purposes only
RevokeCurrencyObject currency = request.getCurrency();
customRevocation.put("namespace", namespace);
customRevocation.put("userId", userId);
customRevocation.put("quantity", String.valueOf(quantity));
customRevocation.put("currencyNamespace", currency.getNamespace());
customRevocation.put("currencyCode", currency.getCurrencyCode());
customRevocation.put("balanceOrigin", currency.getBalanceOrigin());
return RevokeResponse.newBuilder()
.putAllCustomRevocation(customRevocation)
.setStatus(RevocationStatus.SUCCESS.name()).build();
}
}
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,
)
revoke エントリタイプが ENTITLEMENT の場合、以下の関数が実行されます。
- C#
- Go
- Java
- Python
response.CustomRevocation.Add(new Dictionary<string, string>()
{
{ "namespace", request.Namespace },
{ "userId", request.UserId },
{ "quantity", request.Quantity.ToString() },
{ "entitlementId", request.Entitlement.EntitlementId },
{ "itemId", request.Entitlement.ItemId },
{ "sku", request.Entitlement.Sku },
});
func (r *EntitlementRevocation) Revoke(namespace string, userId string, quantity int32, request *pb.RevokeRequest) (*pb.RevokeResponse, error) {
entitlement := request.GetEntitlement()
customRevocation := map[string]string{}
customRevocation["namespace"] = namespace
customRevocation["userId"] = userId
customRevocation["quantity"] = fmt.Sprintf("%d", quantity)
customRevocation["entitlementId"] = entitlement.GetEntitlementId()
customRevocation["itemId"] = entitlement.GetItemId()
customRevocation["sku"] = entitlement.GetSku()
return &pb.RevokeResponse{
Status: StatusSuccess,
CustomRevocation: customRevocation,
}, nil
}
public class EntitlementRevocation implements Revocation {
@Override
public RevokeResponse revoke(String namespace, String userId, int quantity, RevokeRequest request) {
Map<String, String> customRevocation = new HashMap<>();
// execute your logic, this is for demo only
RevokeEntitlementObject entitlement = request.getEntitlement();
customRevocation.put("namespace", namespace);
customRevocation.put("userId", userId);
customRevocation.put("quantity", String.valueOf(quantity));
customRevocation.put("entitlementId", entitlement.getEntitlementId());
customRevocation.put("itemId", entitlement.getItemId());
customRevocation.put("sku", entitlement.getSku());
return RevokeResponse.newBuilder()
.putAllCustomRevocation(customRevocation)
.setStatus(RevocationStatus.SUCCESS.name()).build();
}
}
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,
)
gRPC リクエスト処理の詳細については、こちらをご覧ください。