[RUBY] [Apple Subscription Offer] How to create a promotional offer signature

tl;dr

There was no information about promotional offers for Apple's subscription offers. ** This is Japan's first document. ** **

What is a promotion offer?

WWDC 2019 video

https://developer.apple.com/videos/play/wwdc2019/305

Apple official documentation

https://developer.apple.com/jp/app-store/subscriptions/#subscription-offers

7ec9cdbd-1f10-4e9a-a5a5-c76e2c99f9ee.png

Applicable to customers who are currently using or have used the subscription in the past. With these offers, to increase and maintain the number of users You will have the flexibility to carry out your own promotions. Through the campaign, users who have canceled their subscription can be encouraged to re-subscribe, You can offer an upgrade to another subscription at a special price.

Preparation

Apple official documentation

Setting up a subscription offer

https://developer.apple.com/jp/documentation/storekit/in-app_purchase/setting_up_subscription_offers/

Implementation

** * This time, only the signature creation on the server side will be described **

Apple official documentation

Generating signatures for promotional offers https://developer.apple.com/jp/documentation/storekit/in-app_purchase/generating_a_signature_for_subscription_offers/

What you need to generate a signature

スクリーンショット 2020-09-10 12.16.24.png

■appBundleID Have in an environment variable ■keyIdentifier Have in an environment variable ■productIdentifier Get parameters from the app side ■offerIdentifier Get parameters from the app side ■applicationUsername Get parameters from the app side ■nonce Generate on the server side ■timestamp Generate on the server side

signature

スクリーンショット 2020-09-10 12.45.19.png

Signature generation

Sample code Originally it is divided by method, but emphasis is placed on clarity

require 'openssl'
require 'base64'
require 'securerandom'
require 'json'

#Read as an environment variable, but dare to describe it
private_key = '-----BEGIN PRIVATE KEY-----xxxxxxxxxxxxxxxxxxx-----END PRIVATE KEY-----'

#Prevents the line feed code of the private key read from the environment variable from being escaped
private_key = OpenSSL::PKey::EC.new(private_key.gsub(/\\n/, "\n")))

app_bundle_id = 'xxxx'
key_identifier = 'xxxx'
product_identifier = 'xxxx'
offer_identifier = 'xxxx'
application_username = 'xxxx'
nonce = SecureRandom.uuid
timestamp = (Time.current.to_f * 1000).to_i.to_s

#Invisible separator ('\u2063') Is sandwiched between the parameters and combined

payload = app_bundle_id + "\u{2063}" +
          key_identifier + "\u{2063}" +
          product_identifier + "\u{2063}" +
          offer_identifier + "\u{2063}" +
          application_username + "\u{2063}" +
          nonce + "\u{2063}" +
          timestamp

#signature

# Ruby2.4.If it is 0 or later
# signature = private_key.sign(digest, data)

# SHA-Signed with 256 hashes
signature = private_key.dsa_sign_asn1(OpenSSL::Digest::SHA256.digest(payload))

#Encode to base64
# strict_Use encode64 to erase the line feed code
signature_base64 = Base64.strict_encode64(signature)


#Verification

# OpenSSL::PKey::Generate EC object
ec = OpenSSL::PKey::EC.new(private_key.group)
ec.public_key = private_key.public_key

# SHA-Validate with 256 hashes
digest = OpenSSL::Digest::SHA256.new

#Verify that the signature string of the payload signed with the private key is signature using the public key
ec.verify(digest, signature, payload)

result = { key_identifier: key_identifier, nonce: nonce, timestamp: timestamp, signature: signature_base64 }.to_json

About Elliptic Curve Digital Signature Algorithm (ECDSA)

At first I was thinking about using a gem called ruby_ecdsa https://github.com/DavidEGrayson/ruby_ecdsa

require 'ecdsa'
require 'securerandom'
require 'digest/sha2'

group = ECDSA::Group::Secp256k1

private_key = 1 + SecureRandom.random_number(group.order - 1)
public_key = group.generator.multiply_by_scalar(private_key)

message = 'ECDSA is cool.'
digest = Digest::SHA2.digest(message)

temp_key = 1 + SecureRandom.random_number(group.order - 1)
signature = ECDSA.sign(group, private_key, digest, temp_key)

valid = ECDSA.valid_signature?(public_key, digest, signature)
puts "valid: #{valid}"

However, since the private key was designed assuming a numerical value, I forgot to use it.

Apple official signature creation sample

Sample using JavaScript and Node.js

https://developer.apple.com/documentation/storekit/in-app_purchase/subscriptions_and_offers/generating_a_subscription_offer_signature_on_the_server

Start the server and get a response when you access it

Recommended Posts

[Apple Subscription Offer] How to create a promotional offer signature
How to create a method
[Java] How to create a folder
[Swift5] How to create a splash screen
[rails] How to create a partial template
How to create a database for H2 Database anywhere
[Rails] How to create a graph using lazy_high_charts
How to create pagination for a "kaminari" array
How to create a class that inherits class information
How to create a theme in Liferay 7 / DXP
[1st] How to create a Spring-MVC framework project
How to easily create a pull-down in Rails
[Rails] How to create a Twitter share button
How to create docker-compose
How to create a Java environment in just 3 seconds
[Rails] How to create a signed URL for CloudFront
How to create a JDBC URL (Oracle Database, Thin)
How to create a Spring Boot project in IntelliJ
[Spring Boot] How to create a project (for beginners)
How to create a data URI (base64) in Java
How to leave a comment
How to insert a video
How to create a lightweight container image for Java apps
How to create a form to select a date from the calendar
How to create a placeholder part to use in the IN clause
How to create and launch a Dockerfile for Payara Micro
How to create a jar file or war file using the jar command
How to add columns to a table
Preparing to create a Rails application
How to create a registration / update function where the table crosses
How to make a Java container
[Rails 6] How to create a dynamic form input screen using cocoon
How to sign a Minecraft MOD
How to make a JDBC driver
How to create a new Gradle + Java + Jar project in Intellij 2016.03
How to write a ternary operator
[Swift] How to send a notification
How to make a splash screen
How to make a Jenkins plugin
How to make a Maven project
Try to create a server-client app
How to create a web server on an EC2 instance on AWS
How to make a Java array
How to quickly create a reverse proxy that supports HTTPS with Docker
How to create a query using variables in GraphQL [Using Ruby on Rails]
[Docker] How to create a virtual environment for Rails and Nuxt.js apps
How to create a validator that allows only input to any one field
How to create a server executable JAR and WAR with Spring gradle
[Rails] How to create a table, add a column, and change the column type
How to create a convenient method that utilizes generics and functional interfaces
How to execute a contract using web3j
How to sort a List using Comparator
How to make a Java calendar Summary
A memorandum on how to use Eclipse
How to redo a deployment on Heroku
[Basic] How to write a Dockerfile Self-learning ②
How to insert a video in Rails
How to add a new hash / array
[Introduction to Java] How to write a Java program
How to make a Discord bot (Java)
How to print a Java Word document