{"id":137931,"date":"2021-07-08T00:00:00","date_gmt":"2021-07-08T00:00:00","guid":{"rendered":"https:\/\/adapty.io\/es-storekit-2\/"},"modified":"2025-06-26T13:05:09","modified_gmt":"2025-06-26T13:05:09","slug":"storekit-2","status":"publish","type":"post","link":"https:\/\/adapty.io\/es\/blog\/storekit-2\/","title":{"rendered":"Novedades de la API de StoreKit 2 y c\u00f3mo Apple ha simplificado la integraci\u00f3n de las compras dentro de la aplicaci\u00f3n"},"content":{"rendered":"\n

Durante la Conferencia Mundial de Desarrolladores de Apple (WWDC) 2021 que tuvo lugar recientemente, Apple present\u00f3 una nueva versi\u00f3n de StoreKit 2. Se trata de un framework responsable de las compras en iOS. La proporci\u00f3n de aplicaciones (app) con funciones de compra y suscripci\u00f3n (subscription) dentro de la aplicaci\u00f3n crece constantemente, y Apple ha simplificado significativamente la integraci\u00f3n de las compras dentro de la aplicaci\u00f3n con el lanzamiento de StoreKit 2. Hoy analizaremos el trabajo con StoreKit 2 en la parte del servidor, es decir, con la ayuda de App Store Server API.<\/p>\n\n\n\n

Solicitar autenticaci\u00f3n<\/h2>\n\n\n\n

En la versi\u00f3n actual de la API, necesitas el secreto compartido (shared secret) para enviar una solicitud. Se trata de una cadena secreta fija que puedes obtener en App Store Connect. La nueva versi\u00f3n de la API utiliza el est\u00e1ndar JSON Web Token (JWT) para la autenticaci\u00f3n de las solicitudes.   <\/p>\n\n\n\n

Generaci\u00f3n de clave <\/h3>\n\n\n\n

En primer lugar, crea una clave privada<\/a> que se utilizar\u00e1 para autorizar las solicitudes. Abre App Store Connect y ve a la secci\u00f3n Users y Access, y a continuaci\u00f3n a la pesta\u00f1a Keys. Selecciona el tipo de clave de compras dentro de la aplicaci\u00f3n. Descarga una nueva clave. Tambi\u00e9n necesitar\u00e1s su ID: puedes copiarlo en la misma p\u00e1gina que el ID de la compra dentro de la aplicaci\u00f3n, que se encuentra en la pesta\u00f1a de la API de App Store Connect.<\/p>\n\n\n\n

\"Creating
Crear una clave privada para trabajar con la API de App Store Server<\/em><\/figcaption><\/figure>\n\n\n\n

Crear un token<\/h3>\n\n\n\n

El siguiente paso es crear un token que se utilizar\u00e1 para autorizar las solicitudes. Este proceso se describe detalladamente en la documentaci\u00f3n<\/a>, as\u00ed que no hay raz\u00f3n para prestarle demasiada atenci\u00f3n. Aqu\u00ed tienes un ejemplo de una implementaci\u00f3n ya hecha para Python. Cabe destacar que no tiene sentido generar un nuevo token para cada nueva solicitud. Al crear un token, establece su vida \u00fatil en un m\u00e1ximo de 60 minutos y utiliza el mismo token durante este periodo. <\/p>\n\n\n\n

<\/path><\/path><\/svg><\/span>
import<\/span> time<\/span>,<\/span> uuid<\/span><\/span>\nfrom<\/span> authlib<\/span>.<\/span>jose <\/span>import<\/span> jwt<\/span><\/span>\n<\/span>\nBUNDLE_ID <\/span>=<\/span> <\/span>'<\/span>com.adapty.sample_app<\/span>'<\/span><\/span>\nISSUER_ID <\/span>=<\/span> <\/span>'<\/span>4336a124-f214-4d40-883b-6db275b5e4aa<\/span>'<\/span><\/span>\nKEY_ID <\/span>=<\/span> <\/span>'<\/span>J65UYBDA74<\/span>'<\/span><\/span>\nPRIVATE_KEY <\/span>=<\/span> <\/span>'''<\/span><\/span>\n-----BEGIN PRIVATE KEY-----<\/span><\/span>\nMIGTAgMGByqGSMBHkAQQgR\/fR+3Lkg4...<\/span><\/span>\n-----END PRIVATE KEY-----<\/span><\/span>\n'''<\/span><\/span>\n<\/span>\nissue_time <\/span>=<\/span> <\/span>round<\/span>(<\/span>time<\/span>.<\/span>time<\/span>())<\/span><\/span>\nexpiration_time <\/span>=<\/span> issue_time <\/span>+<\/span> <\/span>60<\/span> <\/span>*<\/span> <\/span>60<\/span> <\/span># 1 hour expiration<\/span><\/span>\n<\/span>\n<\/span>\nheader <\/span>=<\/span> <\/span>{<\/span><\/span>\n <\/span>'<\/span>alg<\/span>'<\/span>:<\/span> <\/span>'<\/span>ES256<\/span>'<\/span>,<\/span><\/span>\n <\/span>'<\/span>kid<\/span>'<\/span>:<\/span> KEY_ID<\/span>,<\/span><\/span>\n <\/span>'<\/span>typ<\/span>'<\/span>:<\/span> <\/span>'<\/span>JWT<\/span>'<\/span><\/span>\n}<\/span><\/span>\n<\/span>\npayload <\/span>=<\/span> <\/span>{<\/span><\/span>\n <\/span>'<\/span>iss<\/span>'<\/span>:<\/span> ISSUER_ID<\/span>,<\/span><\/span>\n <\/span>'<\/span>iat<\/span>'<\/span>:<\/span> issue_time<\/span>,<\/span><\/span>\n <\/span>'<\/span>exp<\/span>'<\/span>:<\/span> expiration_time<\/span>,<\/span><\/span>\n <\/span>'<\/span>aud<\/span>'<\/span>:<\/span> <\/span>'<\/span>appstoreconnect-v1<\/span>'<\/span>,<\/span><\/span>\n <\/span>'<\/span>nonce<\/span>'<\/span>:<\/span> <\/span>str<\/span>(<\/span>uuid<\/span>.<\/span>uuid4<\/span>()),<\/span><\/span>\n <\/span>'<\/span>bid<\/span>'<\/span>:<\/span> BUNDLE_ID<\/span><\/span>\n}<\/span><\/span>\n<\/span>\ntoken_encoded <\/span>=<\/span> jwt<\/span>.<\/span>encode<\/span>(<\/span>header<\/span>,<\/span> payload<\/span>,<\/span> PRIVATE_KEY<\/span>)<\/span><\/span>\ntoken_decoded <\/span>=<\/span> token_encoded<\/span>.<\/span>decode<\/span>()<\/span><\/span>\n<\/span>\nauthorization_header <\/span>=<\/span> <\/span>{<\/span><\/span>\n <\/span>'<\/span>Authorization<\/span>'<\/span>:<\/span> <\/span>f<\/span>'Bearer <\/span>{<\/span>token_decoded<\/span>}<\/span>'<\/span><\/span>\n}<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n

Transacciones firmadas<\/h2>\n\n\n\n

En una nueva versi\u00f3n de la API, todas las transacciones se devuelven en el est\u00e1ndar JSON Web Signature (JWS). Se trata de una cadena formada por tres partes divididas por puntos.  <\/p>\n\n\n\n

    \n
  1. Encabezado Base64.<\/li>\n\n\n\n
  2. Carga \u00fatil de la transacci\u00f3n Base64.<\/li>\n\n\n\n
  3. Firma de la transacci\u00f3n.<\/li>\n<\/ol>\n\n\n\n
    <\/path><\/path><\/svg><\/span>
    Base<\/span>64<\/span>(header) + <\/span>"<\/span>.<\/span>"<\/span> + Base<\/span>64<\/span>(payload) + <\/span>"<\/span>.<\/span>"<\/span> + sign(Base<\/span>64<\/span>(header) + <\/span>"<\/span>.<\/span>"<\/span> + Base<\/span>64<\/span>(payload))<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n

    Encabezado de la transacci\u00f3n<\/h3>\n\n\n\n

    Se necesita un encabezado para asegurarse de que la transacci\u00f3n es aut\u00e9ntica. La clave Alg contiene un algoritmo de encriptaci\u00f3n, la clave x5c contiene una cadena de certificados.<\/p>\n\n\n\n

    <\/path><\/path><\/svg><\/span>
    {<\/span><\/span>\n  <\/span>"<\/span>kid<\/span>"<\/span>:<\/span> <\/span>"<\/span>AMP\/DEV<\/span>"<\/span>,<\/span><\/span>\n  <\/span>"<\/span>alg<\/span>"<\/span>:<\/span> <\/span>"<\/span>ES256<\/span>"<\/span>,<\/span><\/span>\n  <\/span>"<\/span>x5c<\/span>"<\/span>:<\/span> <\/span>[<\/span><\/span>\n    <\/span>"<\/span>MIIEO...<\/span>"<\/span>,<\/span><\/span>\n    <\/span>"<\/span>MIIDK...<\/span>"<\/span><\/span>\n  <\/span>]<\/span><\/span>\n}<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n

    Carga \u00fatil de la transacci\u00f3n<\/h3>\n\n\n\n
    <\/path><\/path><\/svg><\/span>
    {<\/span><\/span>\n  <\/span>"<\/span>transactionId<\/span>"<\/span>:<\/span> <\/span>"<\/span>1000000831360853<\/span>"<\/span>,<\/span><\/span>\n  <\/span>"<\/span>originalTransactionId<\/span>"<\/span>:<\/span> <\/span>"<\/span>1000000806937552<\/span>"<\/span>,<\/span><\/span>\n  <\/span>"<\/span>webOrderLineItemId<\/span>"<\/span>:<\/span> <\/span>"<\/span>1000000063561721<\/span>"<\/span>,<\/span><\/span>\n  <\/span>"<\/span>bundleId<\/span>"<\/span>:<\/span> <\/span>"<\/span>com.adapty.sample_app<\/span>"<\/span>,<\/span><\/span>\n  <\/span>"<\/span>productId<\/span>"<\/span>:<\/span> <\/span>"<\/span>basic_subscription_1_month<\/span>"<\/span>,<\/span><\/span>\n  <\/span>"<\/span>subscriptionGroupIdentifier<\/span>"<\/span>:<\/span> <\/span>"<\/span>27636320<\/span>"<\/span>,<\/span><\/span>\n  <\/span>"<\/span>purchaseDate<\/span>"<\/span>:<\/span> <\/span>1624446341000<\/span>,<\/span><\/span>\n  <\/span>"<\/span>originalPurchaseDate<\/span>"<\/span>:<\/span> <\/span>1619686337000<\/span>,<\/span><\/span>\n  <\/span>"<\/span>expiresDate<\/span>"<\/span>:<\/span> <\/span>1624446641000<\/span>,<\/span><\/span>\n  <\/span>"<\/span>quantity<\/span>"<\/span>:<\/span> <\/span>1<\/span>,<\/span><\/span>\n  <\/span>"<\/span>type<\/span>"<\/span>:<\/span> <\/span>"<\/span>Auto-Renewable Subscription<\/span>"<\/span>,<\/span><\/span>\n  <\/span>"<\/span>appAccountToken<\/span>"<\/span>:<\/span> <\/span>"<\/span>fd12746f-2d3a-46c8-bff8-55b75ed06aca<\/span>"<\/span>,<\/span><\/span>\n  <\/span>"<\/span>inAppOwnershipType<\/span>"<\/span>:<\/span> <\/span>"<\/span>PURCHASED<\/span>"<\/span>,<\/span><\/span>\n  <\/span>"<\/span>signedDate<\/span>"<\/span>:<\/span> <\/span>1624446484882<\/span>,<\/span><\/span>\n  <\/span>"<\/span>offerType<\/span>"<\/span>:<\/span> <\/span>2<\/span>,<\/span><\/span>\n  <\/span>"<\/span>offerIdentifier<\/span>"<\/span>:<\/span> <\/span>"<\/span>basic_subscription_1_month.pay_as_you_go.3_months<\/span>"<\/span><\/span>\n}<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n

    Apple ha cambiado y ampliado el formato de las transacciones. Desde mi punto de vista, ahora es m\u00e1s c\u00f3modo trabajar con ellos. Puedes conocer los detalles del nuevo formato en la documentaci\u00f3n<\/a>. A continuaci\u00f3n, describir\u00e9 los cambios m\u00e1s importantes.    <\/p>\n\n\n\n