waffo-pancake 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +33 -0
- data/README.md +112 -7
- data/lib/waffo/pancake/api/customer_actions.rb +55 -0
- data/lib/waffo/pancake/api/product_actions.rb +64 -0
- data/lib/waffo/pancake/api/promotion_actions.rb +39 -0
- data/lib/waffo/pancake/api/queries.rb +292 -0
- data/lib/waffo/pancake/api/store_actions.rb +64 -0
- data/lib/waffo/pancake/client.rb +74 -165
- data/lib/waffo/pancake/connection.rb +113 -0
- data/lib/waffo/pancake/customer_session.rb +55 -0
- data/lib/waffo/pancake/errors.rb +5 -3
- data/lib/waffo/pancake/requests.rb +91 -0
- data/lib/waffo/pancake/store_checkout.rb +41 -0
- data/lib/waffo/pancake/test_helpers.rb +28 -6
- data/lib/waffo/pancake/version.rb +1 -1
- data/lib/waffo/pancake.rb +1 -0
- metadata +10 -1
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "time"
|
|
4
|
+
|
|
5
|
+
module Waffo
|
|
6
|
+
module Pancake
|
|
7
|
+
module API
|
|
8
|
+
# GraphQL reads, generated from the tables below. The tables follow the schema that
|
|
9
|
+
# introspection reports, not the documentation pages: those show a `version` field on
|
|
10
|
+
# products, `prices` as a scalar and `filter` on the version queries, and none of those exist.
|
|
11
|
+
# test/live/queries_test.rb runs every default selection against the real schema.
|
|
12
|
+
#
|
|
13
|
+
# Filters travel as GraphQL variables, so user data is never interpolated into a query.
|
|
14
|
+
# `fields:` is interpolated: it must come from your code, never from a request.
|
|
15
|
+
module Queries
|
|
16
|
+
PRICES = "prices { currency priceInfo { amount taxCategory } }"
|
|
17
|
+
SUBSCRIPTION_PRICES = "prices { currency priceInfo { amount taxCategory trialAmount } }"
|
|
18
|
+
MEDIA = "media { type url alt thumbnail }"
|
|
19
|
+
AGGREGATE_METRICS = "amount { sum avg min max }"
|
|
20
|
+
|
|
21
|
+
STORE_FIELDS = "id name status slug logo supportEmail website prodEnabled createdAt updatedAt"
|
|
22
|
+
ONETIME_PRODUCT_FIELDS = "id storeId name description #{PRICES} status #{MEDIA} successUrl metadata " \
|
|
23
|
+
"createdAt updatedAt"
|
|
24
|
+
SUBSCRIPTION_PRODUCT_FIELDS = "id storeId name description billingPeriod #{SUBSCRIPTION_PRICES} status " \
|
|
25
|
+
"#{MEDIA} successUrl metadata createdAt updatedAt"
|
|
26
|
+
GROUP_FIELDS = "id storeId name description rules { sharedTrial selfServicePlanChange } productIds " \
|
|
27
|
+
"environment createdAt updatedAt"
|
|
28
|
+
ORDER_FIELDS = "id storeId status testMode buyerEmail merchantProvidedBuyerIdentity orderMerchantExternalId " \
|
|
29
|
+
"currency metadata createdAt updatedAt"
|
|
30
|
+
SUBSCRIPTION_ORDER_FIELDS = "#{ORDER_FIELDS} billingPeriod isInTrial willRenew canReactivate " \
|
|
31
|
+
"currentPeriodStart currentPeriodEnd canceledAt"
|
|
32
|
+
PAYMENT_FIELDS = "id orderId status refundStatus orderMerchantExternalId periodNumber testMode " \
|
|
33
|
+
"snapshotAmountDetails { subtotal listTotal discountAmount taxAmount total currency } createdAt"
|
|
34
|
+
REFUND_FIELDS = "id paymentId ticketId status testMode orderMerchantExternalId " \
|
|
35
|
+
"refundTicketMerchantExternalId pspAmountDetails { amount currency } createdAt"
|
|
36
|
+
REFUND_TICKET_FIELDS = "id status refundTicketMerchantExternalId " \
|
|
37
|
+
"versionData { reason requestedAmount { amount currency } } reviewNote rejectReason " \
|
|
38
|
+
"executedAt createdAt updatedAt"
|
|
39
|
+
CHARGEBACK_FIELDS = "id paymentId orderId storeId status reason reasonCode amount { amount currency } " \
|
|
40
|
+
"testMode createdAt updatedAt"
|
|
41
|
+
CUSTOMER_FIELDS = "id email name country status testMode orderCount subscriptionCount " \
|
|
42
|
+
"activeSubscriptionCount createdAt updatedAt"
|
|
43
|
+
COUPON_FIELDS = "id storeId name code status redeemable discountType percentOff amountOff { amount currency } " \
|
|
44
|
+
"duration durationInPeriods productIds maxRedemptions redeemBy timesRedeemed " \
|
|
45
|
+
"promotionCodesCount createdAt updatedAt"
|
|
46
|
+
PROMOTION_CODE_FIELDS = "id couponId code status batchId timesRedeemed createdAt"
|
|
47
|
+
DISCOUNT_APPLICATION_FIELDS = "id orderId couponId promotionCodeId code sourceType entryChannel appliedVia " \
|
|
48
|
+
"status listAmount { amount currency } discountAmount { amount currency } createdAt"
|
|
49
|
+
EMAIL_DELIVERY_FIELDS = "id storeId eventType eventId recipientType toAddress subject testMode status " \
|
|
50
|
+
"sentAt deliveredAt bounceType errorMessage createdAt"
|
|
51
|
+
CANCEL_REASON_FIELDS = "id orderId storeId reasonCode reasonDetail createdAt"
|
|
52
|
+
MERCHANT_FIELDS = "id email name status createdAt updatedAt"
|
|
53
|
+
STORE_MERCHANT_FIELDS = "id storeId merchantId role memberEmail memberName createdAt updatedAt"
|
|
54
|
+
WEBHOOK_FIELDS = "id storeId channel url events testMode secret createdAt updatedAt"
|
|
55
|
+
RECIPIENT_FIELDS = "id storeId email isPrimary createdAt preferences { notifyNewOrders notifyNewSubscriptions " \
|
|
56
|
+
"notifySubscriptionCanceled notifySubscriptionRenewed notifySubscriptionEnded " \
|
|
57
|
+
"notifySubscriptionPastDue notifySubscriptionUncanceled notifySubscriptionPlanChanged " \
|
|
58
|
+
"notifyChargeback notifyRefundSucceeded }"
|
|
59
|
+
ONETIME_VERSION_FIELDS = "id productId versionNumber name description #{PRICES} #{MEDIA} successUrl metadata " \
|
|
60
|
+
"createdAt isTestVersion isProdVersion"
|
|
61
|
+
SUBSCRIPTION_VERSION_FIELDS = "id productId versionNumber name description billingPeriod " \
|
|
62
|
+
"#{SUBSCRIPTION_PRICES} #{MEDIA} successUrl metadata createdAt isTestVersion " \
|
|
63
|
+
"isProdVersion"
|
|
64
|
+
|
|
65
|
+
# scope: how the list is tied to its owner.
|
|
66
|
+
# :store storeId: String! (defaults to the configured store)
|
|
67
|
+
# :store_slug storeId or storeSlug, both optional
|
|
68
|
+
# :store_opt storeId: String, optional
|
|
69
|
+
# :coupon couponId: String!
|
|
70
|
+
# nil no owner argument
|
|
71
|
+
LISTS = {
|
|
72
|
+
stores: { field: "stores", type: "Store", order: true, fields: STORE_FIELDS },
|
|
73
|
+
merchants: { field: "merchants", type: "Merchant", order: true, single: :merchant, fields: MERCHANT_FIELDS },
|
|
74
|
+
store_merchants: { field: "storeMerchants", type: "StoreMerchant", single: :store_merchant,
|
|
75
|
+
fields: STORE_MERCHANT_FIELDS },
|
|
76
|
+
onetime_products: { field: "onetimeProducts", type: "OnetimeProduct", scope: :store_slug,
|
|
77
|
+
single: :onetime_product, fields: ONETIME_PRODUCT_FIELDS },
|
|
78
|
+
subscription_products: { field: "subscriptionProducts", type: "SubscriptionProduct", scope: :store_slug,
|
|
79
|
+
single: :subscription_product, fields: SUBSCRIPTION_PRODUCT_FIELDS },
|
|
80
|
+
subscription_product_groups: { field: "subscriptionProductGroups", type: "SubscriptionProductGroup",
|
|
81
|
+
scope: :store_slug, single: :subscription_product_group, fields: GROUP_FIELDS },
|
|
82
|
+
onetime_orders: { field: "onetimeOrders", type: "OnetimeOrder", scope: :store_slug, order: true,
|
|
83
|
+
single: :onetime_order, fields: ORDER_FIELDS },
|
|
84
|
+
subscription_orders: { field: "subscriptionOrders", type: "SubscriptionOrder", scope: :store_slug, order: true,
|
|
85
|
+
fields: SUBSCRIPTION_ORDER_FIELDS },
|
|
86
|
+
payments: { field: "payments", type: "Payment", scope: :store_opt, order: true, single: :payment,
|
|
87
|
+
fields: PAYMENT_FIELDS },
|
|
88
|
+
refunds: { field: "refunds", type: "Refund", order: true, single: :refund, fields: REFUND_FIELDS },
|
|
89
|
+
refund_tickets: { field: "refundTickets", type: "RefundTicket", order: true, single: :refund_ticket,
|
|
90
|
+
fields: REFUND_TICKET_FIELDS },
|
|
91
|
+
chargebacks: { field: "chargebacks", type: "Chargeback", order: true, single: :chargeback,
|
|
92
|
+
fields: CHARGEBACK_FIELDS },
|
|
93
|
+
customers: { field: "customers", type: "Customer", scope: :store, order: true, fields: CUSTOMER_FIELDS },
|
|
94
|
+
coupons: { field: "coupons", type: "Coupon", scope: :store, single: :coupon, fields: COUPON_FIELDS },
|
|
95
|
+
promotion_codes: { field: "promotionCodes", type: "PromotionCode", scope: :coupon,
|
|
96
|
+
fields: PROMOTION_CODE_FIELDS },
|
|
97
|
+
discount_applications: { field: "discountApplications", type: "DiscountApplication", scope: :store,
|
|
98
|
+
fields: DISCOUNT_APPLICATION_FIELDS },
|
|
99
|
+
email_deliveries: { field: "emailDeliveries", type: "EmailDelivery", scope: :store, order: true,
|
|
100
|
+
fields: EMAIL_DELIVERY_FIELDS },
|
|
101
|
+
subscription_cancel_reasons: { field: "subscriptionCancelReasons", type: "SubscriptionCancelReason",
|
|
102
|
+
scope: :store, order: true, fields: CANCEL_REASON_FIELDS },
|
|
103
|
+
store_webhooks: { field: "storeWebhooks", type: "StoreWebhook", scope: :store, fields: WEBHOOK_FIELDS }
|
|
104
|
+
}.freeze
|
|
105
|
+
|
|
106
|
+
# method => [GraphQL field, type prefix, reports an amount]. Delivery and reason aggregates only count.
|
|
107
|
+
AGGREGATES = {
|
|
108
|
+
payments_aggregate: ["paymentsAggregate", "Payment", true],
|
|
109
|
+
refunds_aggregate: ["refundsAggregate", "Refund", true],
|
|
110
|
+
onetime_orders_aggregate: ["onetimeOrdersAggregate", "OnetimeOrder", true],
|
|
111
|
+
subscription_orders_aggregate: ["subscriptionOrdersAggregate", "SubscriptionOrder", true],
|
|
112
|
+
payout_tickets_aggregate: ["payoutTicketsAggregate", "PayoutTicket", true],
|
|
113
|
+
settlement_batches_aggregate: ["settlementBatchesAggregate", "SettlementBatch", true],
|
|
114
|
+
webhook_deliveries_aggregate: ["webhookDeliveriesAggregate", "WebhookDelivery", false],
|
|
115
|
+
email_deliveries_aggregate: ["emailDeliveriesAggregate", "EmailDelivery", false],
|
|
116
|
+
subscription_cancel_reasons_aggregate: ["subscriptionCancelReasonsAggregate", "SubscriptionCancelReason", false]
|
|
117
|
+
}.freeze
|
|
118
|
+
|
|
119
|
+
ANALYTICS = {
|
|
120
|
+
order_statistics: "orderStatistics",
|
|
121
|
+
payment_statistics: "paymentStatistics",
|
|
122
|
+
product_statistics: "productStatistics",
|
|
123
|
+
trend_analysis: "trendAnalysis",
|
|
124
|
+
distribution_analysis: "distributionAnalysis",
|
|
125
|
+
customer_analysis: "customerAnalysis",
|
|
126
|
+
tax_analysis: "taxAnalysis",
|
|
127
|
+
subscription_analysis: "subscriptionAnalysis",
|
|
128
|
+
refund_ticket_analysis: "refundTicketAnalysis",
|
|
129
|
+
settlement_analysis: "settlementAnalysis",
|
|
130
|
+
delivery_analysis: "deliveryAnalysis"
|
|
131
|
+
}.freeze
|
|
132
|
+
|
|
133
|
+
OWNER_KEYS = { store: [:store_id], store_slug: %i[store_id store_slug], store_opt: [:store_id],
|
|
134
|
+
coupon: [:coupon_id], nil => [] }.freeze
|
|
135
|
+
|
|
136
|
+
# What a stand-in such as TestHelpers::FakeClient should answer, by method name.
|
|
137
|
+
def self.empty_answers
|
|
138
|
+
@empty_answers ||= begin
|
|
139
|
+
answers = {}
|
|
140
|
+
LISTS.each do |name, spec|
|
|
141
|
+
answers[name] = []
|
|
142
|
+
answers[:"#{name}_count"] = 0
|
|
143
|
+
answers[spec[:single]] = nil if spec[:single]
|
|
144
|
+
end
|
|
145
|
+
answers[:onetime_product_versions] = []
|
|
146
|
+
answers[:subscription_product_versions] = []
|
|
147
|
+
answers[:notification_recipients] = []
|
|
148
|
+
answers[:notification_recipients_count] = 0
|
|
149
|
+
answers[:store] = answers[:me] = answers[:payout_analysis] = nil
|
|
150
|
+
AGGREGATES.each_key { |name| answers[name] = nil }
|
|
151
|
+
ANALYTICS.each_key { |name| answers[name] = nil }
|
|
152
|
+
answers.freeze
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
LISTS.each do |name, spec|
|
|
157
|
+
define_method(name) do |filter: nil, order_by: nil, limit: nil, offset: nil, fields: spec[:fields], **owner|
|
|
158
|
+
arguments = owner_arguments(spec[:scope], owner, name)
|
|
159
|
+
arguments[:limit] = ["Int", limit && Integer(limit)]
|
|
160
|
+
arguments[:offset] = ["Int", offset && Integer(offset)]
|
|
161
|
+
arguments[:filter] = ["#{spec[:type]}Filter", filter]
|
|
162
|
+
raise ArgumentError, "#{name} takes no order_by" if order_by && !spec[:order]
|
|
163
|
+
|
|
164
|
+
arguments[:orderBy] = ["[#{spec[:type]}OrderBy!]", order_by && Array(order_by).map(&:to_s)]
|
|
165
|
+
run_query(spec[:field], arguments, fields) || []
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
define_method(:"#{name}_count") do |filter: nil, **owner|
|
|
169
|
+
arguments = owner_arguments(spec[:scope], owner, name)
|
|
170
|
+
arguments[:filter] = ["#{spec[:type]}Filter", filter]
|
|
171
|
+
run_query("#{spec[:field]}Count", arguments, nil)
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
next unless spec[:single]
|
|
175
|
+
|
|
176
|
+
field = spec[:single].to_s.gsub(/_([a-z])/) { Regexp.last_match(1).upcase }
|
|
177
|
+
define_method(spec[:single]) do |id, fields: spec[:fields]|
|
|
178
|
+
run_query(field, { id: ["String!", id] }, fields)
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
AGGREGATES.each do |name, (field, type, has_amount)|
|
|
183
|
+
default_fields = "count #{AGGREGATE_METRICS if has_amount} " \
|
|
184
|
+
"groups { keys { dimension value } count #{AGGREGATE_METRICS if has_amount} } isTruncated"
|
|
185
|
+
define_method(name) do |filter: nil, group_by: nil, granularity: nil, fields: default_fields|
|
|
186
|
+
run_query(field, {
|
|
187
|
+
filter: ["#{type}Filter", filter],
|
|
188
|
+
groupBy: ["[#{type}Dimension!]", group_by && Array(group_by).map(&:to_s)],
|
|
189
|
+
granularity: ["TimeGranularity", granularity&.to_s]
|
|
190
|
+
}, fields)
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
ANALYTICS.each do |name, field|
|
|
195
|
+
define_method(name) do |fields:, start_date:, end_date:, currency: nil, status: nil, **owner|
|
|
196
|
+
arguments = owner_arguments(:store_slug, owner, name)
|
|
197
|
+
if arguments.values.all? { |_, value| value.nil? }
|
|
198
|
+
raise ArgumentError, "#{name} needs store_id or store_slug"
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
arguments[:filter] = ["AnalyticsFilterInput!", analytics_filter(start_date, end_date, currency, status)]
|
|
202
|
+
run_query(field, arguments, fields)
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
def payout_analysis(fields:, start_date:, end_date:, merchant_id: nil, currency: nil, status: nil)
|
|
207
|
+
run_query("payoutAnalysis", {
|
|
208
|
+
merchantId: ["String", merchant_id],
|
|
209
|
+
filter: ["AnalyticsFilterInput!", analytics_filter(start_date, end_date, currency, status)]
|
|
210
|
+
}, fields)
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
# Defaults to the configured store; pass `slug:` to find a store by slug instead.
|
|
214
|
+
def store(id = nil, slug: nil, fields: STORE_FIELDS)
|
|
215
|
+
id ||= @store_id unless slug
|
|
216
|
+
raise ArgumentError, "id or slug is required" if blank?(id) && blank?(slug)
|
|
217
|
+
|
|
218
|
+
run_query("store", { id: ["String", id], slug: ["String", slug] }, fields)
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
# The merchant the API key belongs to.
|
|
222
|
+
def me(fields: MERCHANT_FIELDS)
|
|
223
|
+
run_query("me", {}, fields)
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
def onetime_product_versions(product_id, fields: ONETIME_VERSION_FIELDS)
|
|
227
|
+
run_query("onetimeProductVersions", { productId: ["String!", product_id] }, fields) || []
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
def subscription_product_versions(product_id, fields: SUBSCRIPTION_VERSION_FIELDS)
|
|
231
|
+
run_query("subscriptionProductVersions", { productId: ["String!", product_id] }, fields) || []
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
def notification_recipients(store_id: @store_id, limit: nil, offset: nil, fields: RECIPIENT_FIELDS)
|
|
235
|
+
run_query("storeNotificationRecipients", {
|
|
236
|
+
storeId: ["String!", require_store_id(store_id)],
|
|
237
|
+
limit: ["Int", limit && Integer(limit)],
|
|
238
|
+
offset: ["Int", offset && Integer(offset)]
|
|
239
|
+
}, fields) || []
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
def notification_recipients_count(store_id: @store_id)
|
|
243
|
+
run_query("storeNotificationRecipientsCount", { storeId: ["String!", require_store_id(store_id)] }, nil)
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
private
|
|
247
|
+
def owner_arguments(scope, owner, name)
|
|
248
|
+
unknown = owner.keys - OWNER_KEYS.fetch(scope)
|
|
249
|
+
# An unknown owner would be dropped, and the query would then read every store.
|
|
250
|
+
raise ArgumentError, "#{name} does not take #{unknown.join(", ")}" unless unknown.empty?
|
|
251
|
+
|
|
252
|
+
store_id = owner.fetch(:store_id, @store_id)
|
|
253
|
+
case scope
|
|
254
|
+
when :store then { storeId: ["String!", require_store_id(store_id)] }
|
|
255
|
+
when :coupon
|
|
256
|
+
raise ArgumentError, "#{name} needs coupon_id" if blank?(owner[:coupon_id])
|
|
257
|
+
|
|
258
|
+
{ couponId: ["String!", owner[:coupon_id]] }
|
|
259
|
+
when :store_opt then { storeId: ["String", store_id] }
|
|
260
|
+
when :store_slug
|
|
261
|
+
# A slug names the store on its own, so the configured store must not join it.
|
|
262
|
+
slug = owner[:store_slug]
|
|
263
|
+
{ storeId: ["String", slug ? nil : store_id], storeSlug: ["String", slug] }
|
|
264
|
+
else {}
|
|
265
|
+
end
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
# `arguments` is { name: [GraphQL type, value] }; a nil value leaves the argument out.
|
|
269
|
+
def run_query(field, arguments, fields)
|
|
270
|
+
given = arguments.reject { |_, (_, value)| value.nil? }
|
|
271
|
+
declared = given.map { |name, (type, _)| "$#{name}: #{type}" }.join(", ")
|
|
272
|
+
passed = given.keys.map { |name| "#{name}: $#{name}" }.join(", ")
|
|
273
|
+
query = "query#{" (#{declared})" unless given.empty?} { #{field}#{"(#{passed})" unless given.empty?}" \
|
|
274
|
+
"#{" { #{fields} }" if fields} }"
|
|
275
|
+
data = graphql(query, given.transform_values(&:last))
|
|
276
|
+
data && data[field]
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
def analytics_filter(start_date, end_date, currency, status)
|
|
280
|
+
filter = { timeRange: { startDate: time_string(start_date), endDate: time_string(end_date) } }
|
|
281
|
+
filter[:currency] = currency if currency
|
|
282
|
+
filter[:status] = status if status
|
|
283
|
+
filter
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
def time_string(value)
|
|
287
|
+
value.respond_to?(:utc) ? value.dup.utc.iso8601 : value.to_s
|
|
288
|
+
end
|
|
289
|
+
end
|
|
290
|
+
end
|
|
291
|
+
end
|
|
292
|
+
end
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Waffo
|
|
4
|
+
module Pancake
|
|
5
|
+
module API
|
|
6
|
+
# Stores, their members, webhook endpoints and notification recipients. Merchant API key.
|
|
7
|
+
module StoreActions
|
|
8
|
+
def create_store(name:, **params)
|
|
9
|
+
action("/v1/actions/store/create-store", name: name, **params)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
# Omitted keeps a field, Waffo::Pancake::NULL clears it, a value sets it.
|
|
13
|
+
def update_store(id:, **params)
|
|
14
|
+
action("/v1/actions/store/update-store", id: id, **params)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def delete_store(id:, **params)
|
|
18
|
+
action("/v1/actions/store/delete-store", id: id, **params)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def add_store_merchant(store_id:, email:, role:, **params)
|
|
22
|
+
action("/v1/actions/store-merchant/add-merchant", store_id: store_id, email: email, role: role, **params)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def remove_store_merchant(store_id:, merchant_id:, **params)
|
|
26
|
+
action("/v1/actions/store-merchant/remove-merchant", store_id: store_id, merchant_id: merchant_id, **params)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def update_store_merchant_role(store_id:, merchant_id:, role:, **params)
|
|
30
|
+
action("/v1/actions/store-merchant/update-role", store_id: store_id, merchant_id: merchant_id, role: role,
|
|
31
|
+
**params)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def add_webhook(store_id:, url:, events:, test_mode:, channel: "http", **params)
|
|
35
|
+
action("/v1/actions/store/add-webhook", store_id: store_id, channel: channel, url: url, events: events,
|
|
36
|
+
test_mode: test_mode, **params)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# `secret: Waffo::Pancake::NULL` clears the secret.
|
|
40
|
+
def update_webhook(id:, **params)
|
|
41
|
+
action("/v1/actions/store/update-webhook", id: id, **params)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def remove_webhook(id:, **params)
|
|
45
|
+
action("/v1/actions/store/remove-webhook", id: id, **params)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def add_notification_recipient(store_id:, email:, **params)
|
|
49
|
+
action("/v1/actions/store-notification-recipient/add-recipient", store_id: store_id, email: email, **params)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def update_notification_recipient(store_id:, recipient_id:, preferences:, **params)
|
|
53
|
+
action("/v1/actions/store-notification-recipient/update-recipient",
|
|
54
|
+
store_id: store_id, recipient_id: recipient_id, preferences: preferences, **params)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def remove_notification_recipient(store_id:, recipient_id:, **params)
|
|
58
|
+
action("/v1/actions/store-notification-recipient/remove-recipient",
|
|
59
|
+
store_id: store_id, recipient_id: recipient_id, **params)
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|