10000 add billing api tutorial · n00bdevel/example-ruby-app@ffdb058 · GitHub
[go: up one dir, main page]

Skip to content

Commit ffdb058

Browse files
committed
add billing api tutorial
1 parent 6740f09 commit ffdb058

File tree

2 files changed

+208
-0
lines changed

2 files changed

+208
-0
lines changed
File renamed without changes.

02 Charging For Your App/app.rb

Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
require 'shopify_api'
2+
require 'sinatra'
3+
require 'httparty'
4+
require 'dotenv'
5+
require 'pry'
6+
7+
8+
class GiftBasket < Sinatra::Base
9+
attr_reader :key,:secret,:tokens
10+
11+
def initialize
12+
Dotenv.load
13+
@key = ENV['API_KEY']
14+
@secret = ENV['API_SECRET']
15+
@app_url = "jamie.ngrok.io"
16+
@tokens = {}
17+
super
18+
end
19+
20+
get '/giftbasket/install' do
21+
shop = request.params['shop']
22+
scopes = "read_orders,read_products,write_products"
23+
24+
# construct the installation URL and redirect the merchant
25+
install_url = "http://#{shop}/admin/oauth/authorize?client_id=#{@key}"\
26+
"&scope=#{scopes}&redirect_uri=https://#{@app_url}/giftbasket/auth"
27+
28+
# redirect to the install_url
29+
redirect install_url
30+
end
31+
32+
get '/giftbasket/auth' do
33+
# extract shop data from request parameters
34+
shop = request.params['shop']
35+
code = request.params['code']
36+
hmac = request.params['hmac']
37+
38+
# perform hmac validation to determine if the request is coming from Shopify
39+
validate_hmac(hmac,request)
40+
41+
# if no access token for this particular shop exist,
42+
# POST the OAuth request and receive the token in the response
43+
get_shop_access_token(shop,key,secret,code)
44+
45+
# now that the session is activated, create a recurring application charge
46+
create_recurring_application_charge
47+
48+
# redirect to the bulk edit URL if there is a token and an activated session and an activated RecurringApplicationCharge
49+
redirect bulk_edit_url
50+
end
51+
52+
get '/activatecharge' do
53+
# store the charge_id from the request
54+
charge_id = request.params['charge_id']
55+
recurring_application_charge = ShopifyAPI::RecurringApplicationCharge.find(charge_id)
56+
recurring_application_charge.status == "accepted" ? recurring_application_charge.activate : redirect(@tokens[:confirmation_url])
57+
58+
# once the charge is activated, subscribe to the order/create webhook and redirect the user back to the bulk edit URL
59+
create_order_webhook
60+
redirect bulk_edit_url
61+
end
< D7AE /code>62+
63+
post '/giftbasket/webhook/order_create' do
64+
# inspect hmac value in header and verify webhook
65+
hmac = request.env['HTTP_X_SHOPIFY_HMAC_SHA256']
66+
67+
request.body.rewind
68+
data = request.body.read
69+
webhook_ok = verify_webhook(hmac, data)
70+
71+
if webhook_ok
72+
shop = request.env['HTTP_X_SHOPIFY_SHOP_DOMAIN']
73+
token = @tokens[shop]
74+
75+
if not token.nil?
76+
session = ShopifyAPI::Session.new(shop, token)
77+
ShopifyAPI::Base.activate_session(session)
78+
else
79+
return [403, "You're not authorized to perform this action."]
80+
end
81+
else
82+
return [403, "You're not authorized to perform this action."]
83+
end
84+
85+
create_usage_charge
86+
# parse the request body as JSON data
87+
json_data = JSON.parse data
88+
89+
line_items = json_data['line_items']
90+
91+
line_items.each do |line_item|
92+
variant_id = line_item['variant_id']
93+
94+
variant = ShopifyAPI::Variant.find(variant_id)
95+
96+
variant.metafields.each do |field|
97+
if field.key == 'ingredients'
98+
items = field.value.split(',')
99+
100+
items.each do |item|
101+
gift_item = ShopifyAPI::Variant.find(item)
102+
gift_item.inventory_quantity = gift_item.inventory_quantity - 1
103+
gift_item.save
104+
end
105+
end
106+
end
107+
end
108+
109+
return [200, "Webhook notification received successfully."]
110+
end
111+
112+
113+
helpers do
114+
def get_shop_access_token(shop,client_id,client_secret,code)
115+
if @tokens[shop].nil?
116+
url = "https://#{shop}/admin/oauth/access_token"
117+
118+
payload = {
119+
client_id: client_id,
120+
client_secret: client_secret,
121+
code: code}
122+
123+
response = HTTParty.post(url, body: payload)
124+
# if the response is successful, obtain the token and store it in a hash
125+
if response.code == 200
126+
@tokens[shop] = response['access_token']
127+
else
128+
return [500, "Something went wrong."]
129+
end
130+
131+
instantiate_session(shop)
132+
end
133+
end
134+
135+
def instantiate_session(shop)
136+
# now that the token is available, instantiate a session
137+
session = ShopifyAPI::Session.new(shop, @tokens[shop])
138+
ShopifyAPI::Base.activate_session(session)
139+
end
140+
141+
def validate_hmac(hmac,request)
142+
h = request.params.reject{|k,_| k == 'hmac' || k == 'signature'}
143+
query = URI.escape(h.sort.collect{|k,v| "#{k}=#{v}"}.join('&'))
144+
digest = OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha256'), @secret, query)
145+
146+
if not (hmac == digest)
147+
10000 return [403, "Authentication failed. Digest provided was: #{digest}"]
148+
end
149+
end
150+
151+
def verify_webhook(hmac, data)
152+
digest = OpenSSL::Digest.new('sha256')
153+
calculated_hmac = Base64.encode64(OpenSSL::HMAC.digest(digest, @secret, data)).strip
154+
155+
hmac == calculated_hmac
156+
end
157+
158+
def create_recurring_application_charge
159+
# checks to see if there is already an RecurringApplicationCharge created and activated
160+
unless ShopifyAPI::RecurringApplicationCharge.current
161+
recurring_application_charge = ShopifyAPI::RecurringApplicationCharge.new(
162+
name: "Gift Basket Plan",
163+
price: 9.99,
164+
return_url: "https:\/\/jordo.ngrok.io\/activatecharge",
165+
test: true,
166+
trial_days: 7,
167+
capped_amount: 100,
168+
terms: "$0.99 for every order created")
169+
170+
# if the new RecurringApplicationCharge saves,redirect the user to the confirmation URL,
171+
# so they can accept or decline the charge
172+
if recurring_application_charge.save
173+
@tokens[:confirmation_url] = recurring_application_charge.confirmation_url
174+
redirect recurring_application_charge.confirmation_url
175+
end
176+
end
177+
end
178+
179+
def bulk_edit_url
180+
bulk_edit_url = "https://www.shopify.com/admin/bulk"\
181+
"?resource_name=ProductVariant"\
182+
"&edit=metafields.test.ingredients:string"
183+
return bulk_edit_url
184+
end
185+
186+
def create_order_webhook
187+
# create webhook for order creation if it doesn't exist
188+
if not ShopifyAPI::Webhook.find(:all).any?
189+
webhook = {
190+
topic: 'orders/create',
191+
address: "https://#{@app_url}/giftbasket/webhook/order_create",
192+
format: 'json'}
193+
194+
ShopifyAPI::Webhook.create(webhook)
195+
end
196+
end
197+
198+
def create_usage_charge
199+
usage_charge = ShopifyAPI::UsageCharge.new(description: "$0.99 for every order created", price: 0.99)
200+
recurring_application_charge = ShopifyAPI::RecurringApplicationCharge.current
201+
usage_charge.prefix_options = {recurring_application_charge_id: recurring_application_charge.id}
202+
usage_charge.save
203+
end
204+
end
205+
206+
end
207+
208+
run GiftBasket.run!

0 commit comments

Comments
 (0)
0