Creates every product in your price list in Stripe and records its shareable payment link on a tab, never twice.
Uses
Google Sheets
Stripe
Turn a price list into Stripe payment links
Give it a Google Sheet of what you sell and run it by hand: each line becomes a product in Stripe with a price and a shareable payment link you can send to a customer. Prices are read as everyday figures such as 49.99 and converted to the smallest unit of the currency you name. Nobody is charged until they pay through a link.
Nothing in your price list is ever changed. A 'Payment links' tab is added alongside it and a line is appended for every link published, with the product, price, link and the Stripe ids behind it. A product already on that tab is skipped, so running again never creates a second link for it. The number of links per run is capped so you can check the first link before the rest, a row priced above your ceiling is skipped so a typo cannot go on sale, and a row with no name or usable price is reported and skipped. If a link is created but the tab cannot be written, the run stops at once. If Stripe fails partway through a product, the run is marked as failed and names it, and the next run finishes that product rather than making a second one.
You need Google Sheets and Stripe connected (Stripe's test mode is a safe first try), and you are asked for the sheet's link, the price list tab's name and your currency when you add it. The tab needs a 'Product' column and a 'Price' column.
sheetUrl to blank.Set sheetUrl to blank.priceListTabName to blank.Set priceListTabName to blank.currency to blank.Set currency to blank.paymentLinksTabName to Payment links.Set paymentLinksTabName to Payment links.maxLinksThisRun to 1.Set maxLinksThisRun to 1.maxPricePerItem to 1000.Set maxPricePerItem to 1000.currency lowercase and save it as currencyCode.Make currency lowercase and save it as currencyCode.currencyCode is not 3If the length of currencyCode is not 3bif, clp, djf, gnf, jpy, kmf, krw, mga, pyg, rwf, ugx, vnd, vuv, xaf, xof, xpf] and save it as zeroDecimalCurrencies.Create a list of [bif, clp, djf, gnf, jpy, kmf, krw, mga, pyg, rwf, ugx, vnd, vuv, xaf, xof, xpf] and save it as zeroDecimalCurrencies.zeroDecimalCurrencies contains currencyCodeIf zeroDecimalCurrencies contains currencyCodecurrencyCode, which has no smaller unit, so every payment link would ask for a hundred times the price. Nothing has been created.Stop and report a problem: This workflow reads prices written the everyday way (49.99) and multiplies by 100 to give Stripe the amount in cents. That is wrong for currencyCode, which has no smaller unit, so every payment link would ask for a hundred times the price. Nothing has been created.maxPricePerItem multiplied by 100 to the nearest integer and save it as maxAmountInSmallestUnit.Round maxPricePerItem multiplied by 100 to the nearest integer and save it as maxAmountInSmallestUnit.linksTabIsNew to true.Set linksTabIsNew to true.createLinksTabErrorIf a step below fails, save the error as createLinksTabErrorpaymentLinksTabName in sheetUrl.Create the tab paymentLinksTabName in sheetUrl.linksTabIsNew to false.Change linksTabIsNew to false.linksTabIsNew is yesIf linksTabIsNew is yespaymentLinksTabName in sheetUrl.Add a row to paymentLinksTabName in sheetUrl.paymentLinksTabName in sheetUrl. Save it as publishedLog.Read column 'A' of paymentLinksTabName in sheetUrl. Save it as publishedLog.alreadyPublished to an empty list.Set alreadyPublished to an empty list.loggedProduct in publishedLog.values:For each loggedProduct in publishedLog.values:loggedProductErrorIf a step below fails, save the error as loggedProductErrorloggedProduct in lowercase to the end of alreadyPublished and save it as alreadyPublished.Add loggedProduct in lowercase to the end of alreadyPublished and save it as alreadyPublished.publishedThisRun to 0.Set publishedThisRun to 0.skippedRows to 0.Set skippedRows to 0.alreadyDoneRows to 0.Set alreadyDoneRows to 0.rememberedProducts.Get the stored value 'stripe_products_awaiting_link'. Save it as rememberedProducts.rememberedProducts.value, or [] if it is empty and save it as productsAwaitingLink.Use rememberedProducts.value, or [] if it is empty and save it as productsAwaitingLink.stripeFailures to an empty list.Set stripeFailures to an empty list.sheetUrl. Save it as priceList.Read the rows of sheetUrl. Save it as priceList.row in priceList.rows:For each row in priceList.rows:publishedThisRun is greater than or equal to maxLinksThisRunIf publishedThisRun is greater than or equal to maxLinksThisRunrowLabel to row row.row_number as text.Set rowLabel to row row.row_number as text.productName to blank.Set productName to blank.productNameErrorIf a step below fails, save the error as productNameErrorrow.Product and save it as productName.Trim row.Product and save it as productName.productName to blank.Change productName to blank.productName is emptyIf productName is emptyrowLabel: the Product column is empty.Show in the output: Skipped rowLabel: the Product column is empty.skippedRows.Add 1 to skippedRows.productName lowercase and save it as productKey.Make productName lowercase and save it as productKey.alreadyPublished contains productKeyIf alreadyPublished contains productKeyalreadyDoneRows.Add 1 to alreadyDoneRows.amountInSmallestUnit to 0.Set amountInSmallestUnit to 0.priceErrorIf a step below fails, save the error as priceErrorrow.Price multiplied by 100 to the nearest integer and save it as amountInSmallestUnit.Round row.Price multiplied by 100 to the nearest integer and save it as amountInSmallestUnit.amountInSmallestUnit to 0.Change amountInSmallestUnit to 0.amountInSmallestUnit is less than or equal to 0If amountInSmallestUnit is less than or equal to 0rowLabel: the Price column must hold a number above 0, written as a plain figure like 49.99 with no currency symbol.Show in the output: Skipped rowLabel: the Price column must hold a number above 0, written as a plain figure like 49.99 with no currency symbol.skippedRows.Add 1 to skippedRows.amountInSmallestUnit is greater than maxAmountInSmallestUnitIf amountInSmallestUnit is greater than maxAmountInSmallestUnitrowLabel: the price is above the 'maxPricePerItem' ceiling of maxPricePerItem as text. Check it is not a typo, then raise the ceiling.Show in the output: Skipped rowLabel: the price is above the 'maxPricePerItem' ceiling of maxPricePerItem as text. Check it is not a typo, then raise the ceiling.skippedRows.Add 1 to skippedRows.stripeProductId to blank.Set stripeProductId to blank.productsAwaitingLink where item.name is productKey and save it as earlierProduct.Keep each item in productsAwaitingLink where item.name is productKey and save it as earlierProduct.earlierProduct isn't emptyIf earlierProduct isn't emptystripeProductId to earlierProduct[0].id.Change stripeProductId to earlierProduct[0].id.createProductErrorIf a step below fails, save the error as createProductErrorstripeProductId.Trim the id from the Stripe “Create a product” step and save it as stripeProductId.rowLabel: Stripe did not create a product for it. createProductError as text.Show in the output: Skipped rowLabel: Stripe did not create a product for it. createProductError as text.productName to the end of stripeFailures and save it as stripeFailures.Add productName to the end of stripeFailures and save it as stripeFailures.productsAwaitingLink and save it as productsAwaitingLink.Add a record of name and id to the end of productsAwaitingLink and save it as productsAwaitingLink.rememberErrorIf a step below fails, save the error as rememberErrorproductsAwaitingLink.Set the stored value 'stripe_products_awaiting_link' to productsAwaitingLink.stripeProductId) but the workflow could not note it down, so this run stopped before going further. If the next run makes a second product with this name, archive the extra one in Stripe. rememberError.message.Stop and report a problem: Stripe made the product '@productName' (stripeProductId) but the workflow could not note it down, so this run stopped before going further. If the next run makes a second product with this name, archive the extra one in Stripe. rememberError.message.stripePriceId to blank.Set stripePriceId to blank.createPriceErrorIf a step below fails, save the error as createPriceErrorstripePriceId.Trim the id from the Stripe “Create a price” step and save it as stripePriceId.rowLabel: Stripe did not set a price on it. createPriceError as text.Show in the output: Skipped rowLabel: Stripe did not set a price on it. createPriceError as text.productName to the end of stripeFailures and save it as stripeFailures.Add productName to the end of stripeFailures and save it as stripeFailures.paymentLink to blank.Set paymentLink to blank.createLinkErrorIf a step below fails, save the error as createLinkErrorpaymentLink.Trim the url from the Stripe “Create a payment link” step and save it as paymentLink.rowLabel: Stripe did not return a payment link. createLinkError as text.Show in the output: Skipped rowLabel: Stripe did not return a payment link. createLinkError as text.productName to the end of stripeFailures and save it as stripeFailures.Add productName to the end of stripeFailures and save it as stripeFailures.priceAsWritten to amountInSmallestUnit divided by 100.Set priceAsWritten to amountInSmallestUnit divided by 100.logWriteErrorIf a step below fails, save the error as logWriteErrorpaymentLinksTabName in sheetUrl.Add a row to paymentLinksTabName in sheetUrl.paymentLink - paste it into that tab by hand, then run this again.Stop and report a problem: A payment link was created for '@productName' but it could not be written to the '@paymentLinksTabName' tab, so this run has stopped rather than carry on unable to tell what it has already published. The link is paymentLink - paste it into that tab by hand, then run this again.productKey to the end of alreadyPublished and save it as alreadyPublished.Add productKey to the end of alreadyPublished and save it as alreadyPublished.productsAwaitingLink where item.name is not productKey and save it as productsAwaitingLink.Keep each item in productsAwaitingLink where item.name is not productKey and save it as productsAwaitingLink.publishedThisRun.Add 1 to publishedThisRun.paymentLink.Show in the output: Published a payment link for '@productName': paymentLink.productsAwaitingLink.Set the stored value 'stripe_products_awaiting_link' to productsAwaitingLink.summary to Published publishedThisRun as text new payment link(s). Skipped skippedRows as text row(s). alreadyDoneRows as text product(s) already had a link.Set summary to Published publishedThisRun as text new payment link(s). Skipped skippedRows as text row(s). alreadyDoneRows as text product(s) already had a link.publishedThisRun is greater than or equal to maxLinksThisRunIf publishedThisRun is greater than or equal to maxLinksThisRunsummary to summary This run stopped at the 'maxLinksThisRun' limit - raise it and run again to carry on down the list.Change summary to summary This run stopped at the 'maxLinksThisRun' limit - raise it and run again to carry on down the list.stripeFailures isn't emptyIf stripeFailures isn't emptystripeFailures with , and save it as failedProducts.Join stripeFailures with , and save it as failedProducts.summary Stripe failed for: failedProducts. Run it again to retry them; a product Stripe already made is reused, not made twice.Stop and report a problem: summary Stripe failed for: failedProducts. Run it again to retry them; a product Stripe already made is reused, not made twice.summary.Finish the run: summary.