Skip to main content

shopify_client/services/subscription/
remote.rs

1use crate::{
2    common::{
3        http::execute_graphql,
4        types::{APIError, RequestCallbacks},
5    },
6    types::subscription::{
7        ActiveSubscriptionsResp, AppPricingInterval, CancelSubscriptionResp,
8        CreateCombinedSubscriptionRequest, CreateRecurringSubscriptionRequest,
9        CreateSubscriptionResp, CreateUsageRecordRequest, CreateUsageRecordResp,
10        CreateUsageSubscriptionRequest, ExtendTrialResp, MoneyInput, UpdateCappedAmountResp,
11    },
12};
13use serde_json::json;
14
15pub async fn create_recurring_subscription(
16    shop_url: &String,
17    version: &String,
18    access_token: &String,
19    callbacks: &RequestCallbacks,
20    request: &CreateRecurringSubscriptionRequest,
21) -> Result<CreateSubscriptionResp, APIError> {
22    let interval = request.interval.unwrap_or(AppPricingInterval::Every30Days);
23    let test = request.test.unwrap_or(false);
24
25    let query = r#"
26        mutation appSubscriptionCreate($name: String!, $returnUrl: URL!, $lineItems: [AppSubscriptionLineItemInput!]!, $test: Boolean, $trialDays: Int) {
27            appSubscriptionCreate(name: $name, returnUrl: $returnUrl, lineItems: $lineItems, test: $test, trialDays: $trialDays) {
28                appSubscription {
29                    id
30                    name
31                    status
32                    lineItems {
33                        id
34                        plan {
35                            pricingDetails {
36                                __typename
37                                ... on AppRecurringPricing {
38                                    price {
39                                        amount
40                                        currencyCode
41                                    }
42                                    interval
43                                }
44                            }
45                        }
46                    }
47                    createdAt
48                    currentPeriodEnd
49                    returnUrl
50                    trialDays
51                    test
52                }
53                confirmationUrl
54                userErrors {
55                    field
56                    message
57                }
58            }
59        }
60    "#.to_string();
61
62    let mut line_item = json!({
63        "plan": {
64            "appRecurringPricingDetails": {
65                "price": {
66                    "amount": request.price,
67                    "currencyCode": request.currency_code
68                },
69                "interval": interval
70            }
71        }
72    });
73
74    // Add discount if provided
75    if let Some(discount) = &request.discount {
76        line_item["plan"]["appRecurringPricingDetails"]["discount"] = json!(discount);
77    }
78
79    let variables = json!({
80        "name": request.name,
81        "returnUrl": request.return_url,
82        "lineItems": [line_item],
83        "test": test,
84        "trialDays": request.trial_days
85    });
86
87    execute_graphql(shop_url, version, access_token, callbacks, query, variables).await
88}
89
90pub async fn create_usage_subscription(
91    shop_url: &String,
92    version: &String,
93    access_token: &String,
94    callbacks: &RequestCallbacks,
95    request: &CreateUsageSubscriptionRequest,
96) -> Result<CreateSubscriptionResp, APIError> {
97    let test = request.test.unwrap_or(false);
98
99    let query = r#"
100        mutation appSubscriptionCreate($name: String!, $returnUrl: URL!, $lineItems: [AppSubscriptionLineItemInput!]!, $test: Boolean, $trialDays: Int) {
101            appSubscriptionCreate(name: $name, returnUrl: $returnUrl, lineItems: $lineItems, test: $test, trialDays: $trialDays) {
102                appSubscription {
103                    id
104                    name
105                    status
106                    lineItems {
107                        id
108                        plan {
109                            pricingDetails {
110                                __typename
111                                ... on AppUsagePricing {
112                                    cappedAmount {
113                                        amount
114                                        currencyCode
115                                    }
116                                    terms
117                                    balanceUsed {
118                                        amount
119                                        currencyCode
120                                    }
121                                    interval
122                                }
123                            }
124                        }
125                    }
126                    createdAt
127                    currentPeriodEnd
128                    returnUrl
129                    trialDays
130                    test
131                }
132                confirmationUrl
133                userErrors {
134                    field
135                    message
136                }
137            }
138        }
139    "#.to_string();
140
141    let variables = json!({
142        "name": request.name,
143        "returnUrl": request.return_url,
144        "lineItems": [{
145            "plan": {
146                "appUsagePricingDetails": {
147                    "cappedAmount": {
148                        "amount": request.capped_amount,
149                        "currencyCode": request.currency_code
150                    },
151                    "terms": request.terms
152                }
153            }
154        }],
155        "test": test,
156        "trialDays": request.trial_days
157    });
158
159    execute_graphql(shop_url, version, access_token, callbacks, query, variables).await
160}
161
162pub async fn create_combined_subscription(
163    shop_url: &String,
164    version: &String,
165    access_token: &String,
166    callbacks: &RequestCallbacks,
167    request: &CreateCombinedSubscriptionRequest,
168) -> Result<CreateSubscriptionResp, APIError> {
169    let interval = request.interval.unwrap_or(AppPricingInterval::Every30Days);
170    let test = request.test.unwrap_or(false);
171
172    let query = r#"
173        mutation appSubscriptionCreate($name: String!, $returnUrl: URL!, $lineItems: [AppSubscriptionLineItemInput!]!, $test: Boolean, $trialDays: Int) {
174            appSubscriptionCreate(name: $name, returnUrl: $returnUrl, lineItems: $lineItems, test: $test, trialDays: $trialDays) {
175                appSubscription {
176                    id
177                    name
178                    status
179                    lineItems {
180                        id
181                        plan {
182                            pricingDetails {
183                                __typename
184                                ... on AppRecurringPricing {
185                                    price {
186                                        amount
187                                        currencyCode
188                                    }
189                                    interval
190                                }
191                                ... on AppUsagePricing {
192                                    cappedAmount {
193                                        amount
194                                        currencyCode
195                                    }
196                                    terms
197                                    balanceUsed {
198                                        amount
199                                        currencyCode
200                                    }
201                                    interval
202                                }
203                            }
204                        }
205                    }
206                    createdAt
207                    currentPeriodEnd
208                    returnUrl
209                    trialDays
210                    test
211                }
212                confirmationUrl
213                userErrors {
214                    field
215                    message
216                }
217            }
218        }
219    "#.to_string();
220
221    let mut recurring_line_item = json!({
222        "plan": {
223            "appRecurringPricingDetails": {
224                "price": {
225                    "amount": request.recurring_price,
226                    "currencyCode": request.recurring_currency_code
227                },
228                "interval": interval
229            }
230        }
231    });
232
233    // Add discount if provided
234    if let Some(discount) = &request.discount {
235        recurring_line_item["plan"]["appRecurringPricingDetails"]["discount"] = json!(discount);
236    }
237
238    let usage_line_item = json!({
239        "plan": {
240            "appUsagePricingDetails": {
241                "cappedAmount": {
242                    "amount": request.capped_amount,
243                    "currencyCode": request.usage_currency_code
244                },
245                "terms": request.terms
246            }
247        }
248    });
249
250    let variables = json!({
251        "name": request.name,
252        "returnUrl": request.return_url,
253        "lineItems": [recurring_line_item, usage_line_item],
254        "test": test,
255        "trialDays": request.trial_days
256    });
257
258    execute_graphql(shop_url, version, access_token, callbacks, query, variables).await
259}
260
261pub async fn cancel_subscription(
262    shop_url: &String,
263    version: &String,
264    access_token: &String,
265    callbacks: &RequestCallbacks,
266    subscription_id: &String,
267    prorate: bool,
268) -> Result<CancelSubscriptionResp, APIError> {
269    let query = r#"
270        mutation appSubscriptionCancel($id: ID!, $prorate: Boolean) {
271            appSubscriptionCancel(id: $id, prorate: $prorate) {
272                appSubscription {
273                    id
274                    name
275                    status
276                    lineItems {
277                        id
278                        plan {
279                            pricingDetails {
280                                __typename
281                                ... on AppRecurringPricing {
282                                    price {
283                                        amount
284                                        currencyCode
285                                    }
286                                    interval
287                                }
288                                ... on AppUsagePricing {
289                                    cappedAmount {
290                                        amount
291                                        currencyCode
292                                    }
293                                    terms
294                                    balanceUsed {
295                                        amount
296                                        currencyCode
297                                    }
298                                    interval
299                                }
300                            }
301                        }
302                    }
303                }
304                userErrors {
305                    field
306                    message
307                }
308            }
309        }
310    "#
311    .to_string();
312
313    let variables = json!({
314        "id": subscription_id,
315        "prorate": prorate
316    });
317
318    execute_graphql(shop_url, version, access_token, callbacks, query, variables).await
319}
320
321pub async fn extend_trial(
322    shop_url: &String,
323    version: &String,
324    access_token: &String,
325    callbacks: &RequestCallbacks,
326    subscription_id: &String,
327    days: i32,
328) -> Result<ExtendTrialResp, APIError> {
329    let query = r#"
330        mutation appSubscriptionTrialExtend($id: ID!, $days: Int!) {
331            appSubscriptionTrialExtend(id: $id, days: $days) {
332                appSubscription {
333                    id
334                    name
335                    status
336                    trialDays
337                }
338                userErrors {
339                    field
340                    message
341                }
342            }
343        }
344    "#
345    .to_string();
346
347    let variables = json!({
348        "id": subscription_id,
349        "days": days
350    });
351
352    execute_graphql(shop_url, version, access_token, callbacks, query, variables).await
353}
354
355pub async fn update_capped_amount(
356    shop_url: &String,
357    version: &String,
358    access_token: &String,
359    callbacks: &RequestCallbacks,
360    line_item_id: &String,
361    capped_amount: &MoneyInput,
362) -> Result<UpdateCappedAmountResp, APIError> {
363    let query = r#"
364        mutation appSubscriptionLineItemUpdate($id: ID!, $cappedAmount: MoneyInput!) {
365            appSubscriptionLineItemUpdate(id: $id, cappedAmount: $cappedAmount) {
366                appSubscription {
367                    id
368                    name
369                    status
370                    lineItems {
371                        id
372                        plan {
373                            pricingDetails {
374                                __typename
375                                ... on AppUsagePricing {
376                                    cappedAmount {
377                                        amount
378                                        currencyCode
379                                    }
380                                    terms
381                                    balanceUsed {
382                                        amount
383                                        currencyCode
384                                    }
385                                    interval
386                                }
387                            }
388                        }
389                    }
390                }
391                userErrors {
392                    field
393                    message
394                }
395            }
396        }
397    "#
398    .to_string();
399
400    let variables = json!({
401        "id": line_item_id,
402        "cappedAmount": {
403            "amount": capped_amount.amount,
404            "currencyCode": capped_amount.currency_code
405        }
406    });
407
408    execute_graphql(shop_url, version, access_token, callbacks, query, variables).await
409}
410
411pub async fn create_usage_record(
412    shop_url: &String,
413    version: &String,
414    access_token: &String,
415    callbacks: &RequestCallbacks,
416    request: &CreateUsageRecordRequest,
417) -> Result<CreateUsageRecordResp, APIError> {
418    let query = r#"
419        mutation appUsageRecordCreate($subscriptionLineItemId: ID!, $price: MoneyInput!, $description: String!, $idempotencyKey: String) {
420            appUsageRecordCreate(subscriptionLineItemId: $subscriptionLineItemId, price: $price, description: $description, idempotencyKey: $idempotencyKey) {
421                appUsageRecord {
422                    id
423                    description
424                    price {
425                        amount
426                        currencyCode
427                    }
428                    createdAt
429                }
430                userErrors {
431                    field
432                    message
433                }
434            }
435        }
436    "#.to_string();
437
438    let variables = json!({
439        "subscriptionLineItemId": request.subscription_line_item_id,
440        "price": {
441            "amount": request.price,
442            "currencyCode": request.currency_code
443        },
444        "description": request.description,
445        "idempotencyKey": request.idempotency_key
446    });
447
448    execute_graphql(shop_url, version, access_token, callbacks, query, variables).await
449}
450
451pub async fn get_active_subscriptions(
452    shop_url: &String,
453    version: &String,
454    access_token: &String,
455    callbacks: &RequestCallbacks,
456) -> Result<ActiveSubscriptionsResp, APIError> {
457    let query = r#"
458        query {
459            currentAppInstallation {
460                activeSubscriptions {
461                    id
462                    name
463                    status
464                    lineItems {
465                        id
466                        plan {
467                            pricingDetails {
468                                __typename
469                                ... on AppRecurringPricing {
470                                    price {
471                                        amount
472                                        currencyCode
473                                    }
474                                    interval
475                                }
476                                ... on AppUsagePricing {
477                                    cappedAmount {
478                                        amount
479                                        currencyCode
480                                    }
481                                    terms
482                                    balanceUsed {
483                                        amount
484                                        currencyCode
485                                    }
486                                    interval
487                                }
488                            }
489                        }
490                    }
491                    createdAt
492                    currentPeriodEnd
493                    returnUrl
494                    trialDays
495                    test
496                }
497            }
498        }
499    "#
500    .to_string();
501
502    let variables = json!({});
503
504    execute_graphql(shop_url, version, access_token, callbacks, query, variables).await
505}