tl;dr
There was no information about promotional offers for Apple's subscription offers. ** This is Japan's first document. ** **
https://developer.apple.com/videos/play/wwdc2019/305
https://developer.apple.com/jp/app-store/subscriptions/#subscription-offers
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.
Setting up a subscription offer
https://developer.apple.com/jp/documentation/storekit/in-app_purchase/setting_up_subscription_offers/
** * This time, only the signature creation on the server side will be described **
Generating signatures for promotional offers https://developer.apple.com/jp/documentation/storekit/in-app_purchase/generating_a_signature_for_subscription_offers/
■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
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
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.
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