Chargebacks & Refunds #

Pragma can also check and process clawbacks for providers that allow returns.

We currently support clawbacks with Epic, Xbox, and PlayStation. See the details for each provider for example configuration.

Xbox and Playstation have internal guides that are only available when your studio has been verified.

Adding a revoke operation #

You will need to add player data operations that remove the original grant(s).

Please note that this example is not intended for production and only includes the basic implementation. You may want to add additional validation and custom errors.

Below builds off the example of granting coins and add an operation: revokeCoins

package documentation

import pragma.applicationRequire
import pragma.playerdata.Component
import pragma.playerdata.Context
import pragma.playerdata.FieldNumber
import pragma.playerdata.PlayerDataContentLibrary
import pragma.playerdata.PlayerDataOperation
import pragma.playerdata.PlayerDataRequest
import pragma.playerdata.PlayerDataResponse
import pragma.playerdata.PlayerDataSubService
import pragma.rpcs.SessionType
import pragma.types.SerializedName

// Component that track a currency type, balance, and platform
@SerializedName("<CURRENT-EPOCH-SECONDS>")
data class Currency(
    @FieldNumber(1) val id: String,
    @FieldNumber(2) var balance: Int,
    @FieldNumber(3) var platformId: String,
): Component

data class RevokeCoins(
    val id: String,
    val amount: Int
): PlayerDataRequest
class RevokeCoinsResponse: PlayerDataResponse

// ...

class FulfillmentOperations(
    contentLibrary: PlayerDataContentLibrary
) : PlayerDataSubService(contentLibrary) {

    // ...

    // Requirement: you must allow the SERVICE SessionType
    // this provides access for the Fulfillment Service to call the operation
    @PlayerDataOperation(sessionTypes = [SessionType.SERVICE])
    fun revokeCoins(
        request: RevokeCoins,
        context: Context
    ): RevokeCoinsResponse {
        /**
         * All operations called through the fulfillment service
         * include the platformId the order was made through
         */
        val platformId = context.getDataBy("platformId") ?: "IN-GAME"
        val entity = context.snapshot.getOrCreateUniqueEntity("Currencies")
        val coinsComponent = entity
            .getComponentsByType<Currency>()
            .singleOrNull { it.id == request.id && it.platformId == platformId }

        applicationRequire(coinsComponent != null) {
            // NOTE - create a custom application error type for this error case
            error("replace with an application error proto")
        }
        coinsComponent.balance -= request.amount
        return RevokeCoinsResponse()
    }
}

Build the project to run player data code generation.

./pragma build -s

Add revoke to fulfillment mapping content #

The fulfillment content must be defined in FulfillmentMappingSpecs.json.

[project]/content/src/FulfillmentMappingSpecs.json

[
  {
    "skuId": "GoldBundle100",
    "operations": {
        "fulfillmentOperation": {
            "grantCoins": {
                "id": "gold",
                "amount": "100"
            }
        },
        "revocationOperation" : {
            "revokeCoins" : {
                "id" : "gold",
                "amount" : "100"
            }
        }
    }
  }
]

Run the content apply command to validate and generate content catalogs.

./pragma content apply