------------------------------------------- ------------------------------------------- {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RecordWildCards #-} -- | -- Module : Circle.Client -- Copyright : (c) Dylan Martin, 2022 -- Maintainer : dmarticus@gmail.com -- Stability : experimental -- Portability : POSIX -- -- < https:/\/\developers.circle.com/developer/v1/docs > module Circle.Client where import Circle.Types import Data.Aeson (eitherDecode, encode) import Data.Aeson.Types (FromJSON) import qualified Data.ByteString.Char8 as BS8 import qualified Data.ByteString.Lazy.Char8 as BSL import Data.Maybe (fromJust, isNothing) import qualified Data.Text as T import Data.UUID import qualified Data.UUID as UUID import Network.HTTP.Client ( Manager, Request (method, queryString, requestBody), RequestBody (RequestBodyLBS), Response (responseBody), applyBearerAuth, httpLbs, newManager, parseRequest, requestHeaders, ) import Network.HTTP.Client.TLS (tlsManagerSettings) import Network.HTTP.Types.Header (hAccept, hContentType) import qualified Network.HTTP.Types.Method as NHTM --------------------------------------------------------------- -- /businessAccount/banks/wires endpoint --------------------------------------------------------------- -- | Create a business bank account for a wire -- https://developers.circle.com/reference/createbusinesswireaccount createBusinessWireAccount :: WireAccountRequestBody -> CircleAPIRequest WireAccountRequest TupleBS8 BSL.ByteString createBusinessWireAccount wireAccountBody = do mkCircleAPIRequest NHTM.methodPost url params where url = "businessAccount/banks/wires" params = case wireAccountBody of USBankAccount usBankAccountBody -> Params (Just $ Body (encode usBankAccountBody)) [] IBANBankAccount ibanBankAccountBody -> Params (Just $ Body (encode ibanBankAccountBody)) [] NonIBANBankAccount nonIBANBankAccountBody -> Params (Just $ Body (encode nonIBANBankAccountBody)) [] -- | Get a list of business account wire accounts -- https://developers.circle.com/reference/listbusinesswireaccounts listBusinessWireAccounts :: CircleAPIRequest WireAccountsRequest TupleBS8 BSL.ByteString listBusinessWireAccounts = do mkCircleAPIRequest NHTM.methodGet url params where url = "businessAccount/banks/wires" params = Params Nothing [] -- | Get a single business account wire account, accepts the wire account Id as a parameter -- https://developers.circle.com/reference/getbusinesswireaccount getBusinessWireAccount :: UUID -> CircleAPIRequest WireAccountRequest TupleBS8 BSL.ByteString getBusinessWireAccount wireAccountId = do mkCircleAPIRequest NHTM.methodGet url params where url = "businessAccount/banks/wires/" <> UUID.toText wireAccountId params = Params Nothing [] -- | Get the wire transfer instructions into the Circle business bank account given your bank account id. -- https://developers.circle.com/reference/getbusinesswireaccountinstructions getBusinessWireAccountInstructions :: UUID -> CircleAPIRequest WireInstructionsRequest TupleBS8 BSL.ByteString getBusinessWireAccountInstructions wireAccountId = do mkCircleAPIRequest NHTM.methodGet url params where url = "businessAccount/banks/wires/" <> UUID.toText wireAccountId <> "/instructions" params = Params Nothing [] --------------------------------------------------------------- -- /businessAccount/balances endpoint --------------------------------------------------------------- -- | List all business balances -- https://developers.circle.com/reference/listbusinesspayouts listAllBusinessBalances :: CircleAPIRequest BalanceRequest TupleBS8 BSL.ByteString listAllBusinessBalances = do mkCircleAPIRequest NHTM.methodGet url params where url = "businessAccount/balances" params = Params Nothing [] --------------------------------------------------------------- -- /configuration endpoint --------------------------------------------------------------- -- | Get configuration info -- https://developers.circle.com/reference/getaccountconfig getConfigurationInfo :: CircleAPIRequest ConfigurationRequest TupleBS8 BSL.ByteString getConfigurationInfo = do mkCircleAPIRequest NHTM.methodGet url params where url = "configuration" params = Params Nothing [] --------------------------------------------------------------- -- /encryption/public endpoint --------------------------------------------------------------- -- | Get encryption info -- https://developers.circle.com/reference/getpublickey getPublicKey :: CircleAPIRequest EncryptionRequest TupleBS8 BSL.ByteString getPublicKey = do mkCircleAPIRequest NHTM.methodGet url params where url = "encryption/public" params = Params Nothing [] --------------------------------------------------------------- -- /channels endpoint --------------------------------------------------------------- -- | List all channels -- https://developers.circle.com/reference/listchannels listAllChannels :: CircleAPIRequest ChannelsRequest TupleBS8 BSL.ByteString listAllChannels = do mkCircleAPIRequest NHTM.methodGet url params where url = "channels" params = Params Nothing [] --------------------------------------------------------------- -- /stablecoins endpoint --------------------------------------------------------------- -- | List all stablecoins -- https://developers.circle.com/reference/listchannels listAllStablecoins :: CircleAPIRequest StablecoinsRequest TupleBS8 BSL.ByteString listAllStablecoins = do mkCircleAPIRequest NHTM.methodGet url params where url = "stablecoins" params = Params Nothing [] --------------------------------------------------------------- -- /notifications/subscriptions endpoint --------------------------------------------------------------- -- | List all subscriptions -- https://developers.circle.com/reference/listsubscriptions listAllNotificationSubscriptions :: CircleAPIRequest SubscriptionsRequest TupleBS8 BSL.ByteString listAllNotificationSubscriptions = do mkCircleAPIRequest NHTM.methodGet url params where url = "notifications/subscriptions" params = Params Nothing [] -- | Create new subscription -- https://developers.circle.com/reference/createsubscribtion createSubscription :: SubscriptionRequestBody -> CircleAPIRequest SubscriptionRequest TupleBS8 BSL.ByteString createSubscription subscriptionBody = do mkCircleAPIRequest NHTM.methodPost url params where url = "notifications/subscriptions" params = Params (Just $ Body (encode subscriptionBody)) [] -- | Delete subscription -- https://developers.circle.com/reference/deletesubscribtion deleteSubscription :: UUID -> CircleAPIRequest SubscriptionsRequest TupleBS8 BSL.ByteString deleteSubscription resourceId = do mkCircleAPIRequest NHTM.methodDelete url params where url = "notifications/subscriptions" <> UUID.toText resourceId params = Params Nothing [] --------------------------------------------------------------- -- /businessAccount/payouts endpoint --------------------------------------------------------------- -- | Lists all payouts made from a given business account -- https://developers.circle.com/reference/listbusinesspayouts listAllBusinessAccountPayouts :: CircleAPIRequest PayoutsRequest TupleBS8 BSL.ByteString listAllBusinessAccountPayouts = do mkCircleAPIRequest NHTM.methodGet url params where url = "businessAccount/payouts" params = Params Nothing [] -- | Gets a specific payout associated with a business account -- https://developers.circle.com/reference/getbusinesspayout getBusinessAccountPayout :: UUID -> CircleAPIRequest PayoutRequest TupleBS8 BSL.ByteString getBusinessAccountPayout payoutId = do mkCircleAPIRequest NHTM.methodGet url params where url = "businessAccount/payouts" <> UUID.toText payoutId params = Params Nothing [] -- | Creates a business account payout -- https://developers.circle.com/reference/createbusinesspayout createBusinessAccountPayout :: BusinessPayoutRequestBody -> CircleAPIRequest PayoutRequest TupleBS8 BSL.ByteString createBusinessAccountPayout payoutBody = do mkCircleAPIRequest NHTM.methodPost url params where url = "businessAccount/payouts" params = Params (Just $ Body (encode payoutBody)) [] --------------------------------------------------------------- -- /businessAccount/transfers endpoint --------------------------------------------------------------- -- | Searches for transfers from your business account. -- If the date parameters are omitted, returns the most recent transfers. -- This endpoint returns up to 50 transfers in descending chronological order or pageSize, if provided. -- https://developers.circle.com/reference/listbusinesstransfers listAllBusinessAccountTransfers :: CircleAPIRequest TransfersRequest TupleBS8 BSL.ByteString listAllBusinessAccountTransfers = do mkCircleAPIRequest NHTM.methodGet url params where url = "businessAccount/transfers" params = Params Nothing [] -- | Get a business account transfer based on a transfer ID -- https://developers.circle.com/reference/getbusinesstransfer getBusinessAccountTransfer :: UUID -> CircleAPIRequest TransferRequest TupleBS8 BSL.ByteString getBusinessAccountTransfer transferId = do mkCircleAPIRequest NHTM.methodGet url params where url = "businessAccount/transfers/" <> T.pack (show transferId) params = Params Nothing [] -- | Create a new transfer -- https://developers.circle.com/reference/createbusinesstransfer createBusinessAccountTransfer :: BusinessTransferRequestBody -> CircleAPIRequest TransferRequest TupleBS8 BSL.ByteString createBusinessAccountTransfer transferBody = do mkCircleAPIRequest NHTM.methodPost url params where url = "businessAccount/transfers" params = Params (Just $ Body (encode transferBody)) [] --------------------------------------------------------------- -- /businessAccount/wallets/addresses endpoint --------------------------------------------------------------- -- | List all deposit addresses -- https://developers.circle.com/developer/v1/reference/getbusinessdepositaddress listAllBusinessAccountDepositAddresses :: CircleAPIRequest DepositAddressesRequest TupleBS8 BSL.ByteString listAllBusinessAccountDepositAddresses = do mkCircleAPIRequest NHTM.methodGet url params where url = "businessAccount/wallets/addresses/deposit" params = Params Nothing [] -- | Create new business account deposit address -- Generates a new blockchain address for a wallet for a given currency/chain pair. -- Circle may reuse addresses on blockchains that support reuse. -- For example, if you're requesting two addresses for depositing USD and ETH, both on Ethereum, -- you may see the same Ethereum address returned. -- Depositing cryptocurrency to a generated address will credit the associated wallet with the value of the deposit. -- https://developers.circle.com/developer/v1/reference/createbusinessdepositaddress createBusinessAccountDepositAddress :: DepositAddressRequestBody -> CircleAPIRequest DepositAddressRequest TupleBS8 BSL.ByteString createBusinessAccountDepositAddress depositAddressBody = do mkCircleAPIRequest NHTM.methodPost url params where url = "businessAccount/wallets/addresses/deposit" params = Params (Just $ Body (encode depositAddressBody)) [] -- | List all recipient addresses -- Returns a list of recipient addresses that have each been verified and are eligible for transfers. -- Any recipient addresses pending verification are not included in the response. -- https://developers.circle.com/developer/v1/reference/listbusinessrecipientaddresses listAllBusinessAccountRecipientAddresses :: CircleAPIRequest RecipientAddressesRequest TupleBS8 BSL.ByteString listAllBusinessAccountRecipientAddresses = do mkCircleAPIRequest NHTM.methodGet url params where url = "businessAccount/wallets/addresses/recipient" params = Params Nothing [] -- | Create a new recipient address -- Stores an external blockchain address. Once added, the recipient address must be verified to ensure that you know and trust each new address. -- https://developers.circle.com/developer/v1/reference/createbusinessrecipientaddress createBusinessAccountRecipientAddress :: RecipientAddressRequestBody -> CircleAPIRequest RecipientAddressRequest TupleBS8 BSL.ByteString createBusinessAccountRecipientAddress recipientAddressBody = do mkCircleAPIRequest NHTM.methodPost url params where url = "businessAccount/wallets/addresses/recipient" params = Params (Just $ Body (encode recipientAddressBody)) [] --------------------------------------------------------------- -- /businessAccount/deposits Endpoint --------------------------------------------------------------- -- | List all deposits -- Searches for deposits sent to your business account. If the date parameters are omitted, returns the most recent deposits. -- This endpoint returns up to 50 deposits in descending chronological order or pageSize, if provided. -- https://developers.circle.com/developer/v1/reference/listbusinessdeposits listAllBusinessAccountDeposits :: CircleAPIRequest DepositsRequest TupleBS8 BSL.ByteString listAllBusinessAccountDeposits = do mkCircleAPIRequest NHTM.methodGet url params where url = "businessAccount/deposits" params = Params Nothing [] --------------------------------------------------------------- -- /businessAccount/banks/signet endpoint (Production-Only) --------------------------------------------------------------- -- TODO how to test these if no sandbox instance? -- | Create a signet bank account -- https://developers.circle.com/developer/v1/reference/createbusinesssignetaccount createSignetBankAccount :: SignetBankAccountRequestBody -> CircleAPIRequest SignetBankAccountRequest TupleBS8 BSL.ByteString createSignetBankAccount signetBankAccountBody = do mkCircleAPIRequest NHTM.methodPost url params where url = "businessAccount/banks/signet" params = Params (Just $ Body (encode signetBankAccountBody)) [] -- | Get a list of Signet accounts -- https://developers.circle.com/developer/v1/reference/listbusinesssignetaccounts listSignetAccounts :: CircleAPIRequest SignetBankAccountsRequest TupleBS8 BSL.ByteString listSignetAccounts = do mkCircleAPIRequest NHTM.methodGet url params where url = "businessAccount/banks/signet" params = Params Nothing [] -- | Get a single Signet bank account, accepts the Signet bank account Id as a parameter -- https://developers.circle.com/developer/v1/reference/getbusinesssignetaccount getSignetAccount :: UUID -> CircleAPIRequest SignetBankAccountRequestBody TupleBS8 BSL.ByteString getSignetAccount signetBankAccountId = do mkCircleAPIRequest NHTM.methodGet url params where url = "businessAccount/banks/signet/" <> UUID.toText signetBankAccountId params = Params Nothing [] -- | Get the Signet transfer instructions into the Circle bank account given your bank account id (only available on Production now). -- https://developers.circle.com/developer/v1/reference/getbusinesssignetaccountinstructions getSignetAccountInstructions :: UUID -> CircleAPIRequest SignetBankInstructionsResponseData TupleBS8 BSL.ByteString getSignetAccountInstructions signetBankAccountId = do mkCircleAPIRequest NHTM.methodGet url params where url = "businessAccount/banks/signet/" <> UUID.toText signetBankAccountId <> "/instructions" params = Params Nothing [] --------------------------------------------------------------- -- /businessAccount/banks/sen endpoint (Silvergate SEN operations) --------------------------------------------------------------- -- | Create a bank account for a SEN -- https://developers.circle.com/developer/v1/reference/createbusinesssenaccount createSENAccount :: SENAccountRequestBody -> CircleAPIRequest SENAccountRequest TupleBS8 BSL.ByteString createSENAccount senAccountBody = do mkCircleAPIRequest NHTM.methodPost url params where url = "businessAccount/banks/sen" params = Params (Just $ Body (encode senAccountBody)) [] -- | Get a list of SEN accounts -- https://developers.circle.com/developer/v1/reference/listbusinesssenaccounts listSENAccounts :: CircleAPIRequest SENAccountsRequest TupleBS8 BSL.ByteString listSENAccounts = do mkCircleAPIRequest NHTM.methodGet url params where url = "businessAccount/banks/sen" params = Params Nothing [] -- | Get a single SEN account, accepts the SEN account Id as a parameter -- https://developers.circle.com/developer/v1/reference/getbusinesssenaccount getSENAccount :: UUID -> CircleAPIRequest SENAccountRequest TupleBS8 BSL.ByteString getSENAccount senAccountId = do mkCircleAPIRequest NHTM.methodGet url params where url = "businessAccount/banks/sen/" <> UUID.toText senAccountId params = Params Nothing [] -- | Get the SEN transfer instructions into the Circle bank account given your bank account id. -- https://developers.circle.com/developer/v1/reference/getbusinesssenaccountinstructions getSENAccountInstructions :: UUID -> CircleAPIRequest SENInstructionsRequest TupleBS8 BSL.ByteString getSENAccountInstructions senAccountId = do mkCircleAPIRequest NHTM.methodGet url params where url = "businessAccount/banks/sen/" <> UUID.toText senAccountId <> "/instructions" params = Params Nothing [] --------------------------------------------------------------- -- /payments endpoint --------------------------------------------------------------- -- | List all payments -- https://developers.circle.com/developer/v1/reference/listpayments listAllPayments :: CircleAPIRequest PaymentsRequest TupleBS8 BSL.ByteString listAllPayments = do mkCircleAPIRequest NHTM.methodGet url params where url = "payments" params = Params Nothing [] -- | Create a payment (fiat or Crypto) -- https://developers.circle.com/developer/v1/reference/payments-payments-create createPayment :: CreatePaymentRequestBody -> CircleAPIRequest PaymentRequest TupleBS8 BSL.ByteString createPayment createPaymentBody = do mkCircleAPIRequest NHTM.methodPost url params where url = "payments" params = Params (Just $ Body (encode createPaymentBody)) [] -- | Get a payment (fiat or Crypto) -- https://developers.circle.com/developer/v1/reference/payments-payments-get-id getPayment :: UUID -> CircleAPIRequest PaymentRequest TupleBS8 BSL.ByteString getPayment paymentId = do mkCircleAPIRequest NHTM.methodGet url params where url = "payments/" <> UUID.toText paymentId params = Params Nothing [] -- | Cancel a fiat payment -- https://developers.circle.com/developer/v1/reference/payments-payments-cancel-id cancelPayment :: UUID -> CancelPaymentRequestBody -> CircleAPIRequest PaymentRequest TupleBS8 BSL.ByteString cancelPayment paymentId cancelPaymentBody = do mkCircleAPIRequest NHTM.methodPost url params where url = "payments/" <> UUID.toText paymentId <> "/cancel" params = Params (Just $ Body (encode cancelPaymentBody)) [] -- | Refund a fiat payment -- https://developers.circle.com/developer/v1/reference/payments-payments-refund-id refundPayment :: UUID -> RefundPaymentRequestBody -> CircleAPIRequest PaymentRequest TupleBS8 BSL.ByteString refundPayment paymentId refundPaymentBody = do mkCircleAPIRequest NHTM.methodPost url params where url = "payments/" <> UUID.toText paymentId <> "/refund" params = Params (Just $ Body (encode refundPaymentBody)) [] -- TODO add capture payment method? --------------------------------------------------------------- -- /mock/payments endpoints --------------------------------------------------------------- -- TODO constrain these method to run in the sandbox only. Would be cool to do the same thing with the Production only methods -- | Create mock wire payment SANDBOX ONLY -- In the sandbox environment, initiate a mock wire payment that mimics the behavior of funds sent through the bank (wire) account linked to master wallet. -- https://developers.circle.com/developer/v1/reference/createmockwirepayment createMockWirePayment :: MockSenOrWirePaymentRequestBody -> CircleAPIRequest MockPaymentRequest TupleBS8 BSL.ByteString createMockWirePayment wireBody = do mkCircleAPIRequest NHTM.methodPost url params where url = "mocks/payments/wire" params = Params (Just $ Body (encode wireBody)) [] -- | Create mock SEPA payment SANDBOX ONLY (in Beta) -- In the sandbox environment, initiate a mock SEPA payment that mimics the behavior of funds sent through the bank (SEPA) account linked to master wallet. -- https://developers.circle.com/developer/v1/reference/createmocksepapayment createMockSEPAPayment :: MockSEPAPaymentRequestBody -> CircleAPIRequest MockPaymentRequest TupleBS8 BSL.ByteString createMockSEPAPayment sepaBody = do mkCircleAPIRequest NHTM.methodPost url params where url = "mocks/payments/sepa" params = Params (Just $ Body (encode sepaBody)) [] -- | Create mock Silvergate payment SANDBOX ONLY -- In the sandbox environment, initiate a mock SEN transfer that mimics the behavior of funds sent through the Silvergate SEN account linked to master wallet. -- https://developers.circle.com/developer/v1/reference/createmocksenpayment createMockSilvergatePayment :: MockSenOrWirePaymentRequestBody -> CircleAPIRequest MockPaymentRequest TupleBS8 BSL.ByteString createMockSilvergatePayment senBody = do mkCircleAPIRequest NHTM.methodPost url params where url = "mocks/payments/sen" params = Params (Just $ Body (encode senBody)) [] --------------------------------------------------------------- -- /mock/ach endpoint --------------------------------------------------------------- -- | Create mock ACH account SANDBOX ONLY -- In the sandbox environment, create a mock ACH account and retrieve a processor token that can be used to link an ACH account. -- https://developers.circle.com/developer/v1/reference/createmockachaccount-1 createMockACHBankAccount :: CreateMockACHBankAccountRequestBody -> CircleAPIRequest MockAccountRequest TupleBS8 BSL.ByteString createMockACHBankAccount achPaymentBody = do mkCircleAPIRequest NHTM.methodPost url params where url = "mocks/ach/account" params = Params (Just $ Body (encode achPaymentBody)) [] --------------------------------------------------------------- -- /mock/cards/chargebacks endpoint --------------------------------------------------------------- -- | Create mock chargeback SANDBOX ONLY -- In the sandbox environment, initiate a mock chargeback of a specified payment. -- The entire payment will be charged back for its full value. The payment must be in the paid state -- (otherwise the endpoint will return a 404), and each payment can only be charged back once -- (otherwise the endpoint will return a 409). This endpoint is only available in the sandbox environment. -- https://developers.circle.com/developer/v1/reference/payments-chargebacks-mock-create createMockChargeback :: UUID -> CircleAPIRequest MockChargebackRequest TupleBS8 BSL.ByteString createMockChargeback paymentId = do mkCircleAPIRequest NHTM.methodGet url params where url = "mocks/cards/chargebacks" params = Params (Just $ Body (encode paymentId)) [] --------------------------------------------------------------- -- /transfers endpoint (On-chain transfers) --------------------------------------------------------------- -- | Searches for transfers. -- Searches for transfers involving the provided wallets. If no wallet ids -- are provided, searches all wallets associated with your Circle API -- account. If the date parameters are omitted, returns the most recent -- transfers. This endpoint returns up to 50 transfers in descending -- chronological order or pageSize, if provided. -- https://developers.circle.com/developer/v1/reference/listtransfers-1 listAllOnChainTransfers :: CircleAPIRequest OnChainTransfersRequest TupleBS8 BSL.ByteString listAllOnChainTransfers = do mkCircleAPIRequest NHTM.methodGet url params where url = "transfers" params = Params Nothing [] -- | Get a business account transfer based on a transfer ID -- https://developers.circle.com/developer/v1/reference/gettransfer getOnChainTransfer :: UUID -> CircleAPIRequest TransferRequest TupleBS8 BSL.ByteString getOnChainTransfer transferId = do mkCircleAPIRequest NHTM.methodGet url params where url = "transfers/" <> UUID.toText transferId params = Params Nothing [] -- | Create an on-chain transfer (i.e. a crypto payment) -- https://developers.circle.com/developer/v1/reference/accounts-transfers-create createOnChainTransfer :: OnChainTransferRequestBody -> CircleAPIRequest TransferRequest TupleBS8 BSL.ByteString createOnChainTransfer onChainTransferBody = do mkCircleAPIRequest NHTM.methodPost url params where url = "transfers" params = Params (Just $ Body (encode onChainTransferBody)) [] --------------------------------------------------------------- -- /cards endpoint --------------------------------------------------------------- -- | List all cards -- https://developers.circle.com/developer/v1/reference/listcards listAllCards :: CircleAPIRequest CardsRequest TupleBS8 BSL.ByteString listAllCards = do mkCircleAPIRequest NHTM.methodGet url params where url = "cards" params = Params Nothing [] -- | Get a card -- https://developers.circle.com/developer/v1/reference/payments-cards-get-id getCard :: UUID -> CircleAPIRequest CardRequest TupleBS8 BSL.ByteString getCard cardId = do mkCircleAPIRequest NHTM.methodGet url params where url = "cards/" <> UUID.toText cardId params = Params Nothing [] -- | Create a card -- https://developers.circle.com/developer/v1/reference/payments-cards-create createCard :: CreateCardRequestBody -> CircleAPIRequest CardRequest TupleBS8 BSL.ByteString createCard createCardBody = do mkCircleAPIRequest NHTM.methodPost url params where url = "cards" params = Params (Just $ Body (encode createCardBody)) [] -- | Update a card -- https://developers.circle.com/developer/v1/reference/updatecard updateCard :: UUID -> UpdateCardRequestBody -> CircleAPIRequest CardRequest TupleBS8 BSL.ByteString updateCard cardId updateCardBody = do mkCircleAPIRequest NHTM.methodPut url params where url = "cards/" <> UUID.toText cardId params = Params (Just $ Body (encode updateCardBody)) [] --------------------------------------------------------------- -- banks/wires endpoint --------------------------------------------------------------- -- | Create a bank account for a wire -- https://developers.circle.com/developer/v1/reference/createwireaccount createWireAccount :: WireAccountRequestBody -> CircleAPIRequest WireAccountRequest TupleBS8 BSL.ByteString createWireAccount wireAccountBody = do mkCircleAPIRequest NHTM.methodPost url params where url = "banks/wires" params = case wireAccountBody of USBankAccount usBankAccountBody -> Params (Just $ Body (encode usBankAccountBody)) [] IBANBankAccount ibanBankAccountBody -> Params (Just $ Body (encode ibanBankAccountBody)) [] NonIBANBankAccount nonIBANBankAccountBody -> Params (Just $ Body (encode nonIBANBankAccountBody)) [] -- | Get a single wire account, accepts the wire account Id as a parameter -- https://developers.circle.com/developer/v1/reference/getwireaccount-1 getWireAccount :: UUID -> CircleAPIRequest WireAccountRequest TupleBS8 BSL.ByteString getWireAccount wireAccountId = do mkCircleAPIRequest NHTM.methodGet url params where url = "banks/wires/" <> UUID.toText wireAccountId params = Params Nothing [] -- | Get the wire transfer instructions into the Circle bank account given your bank account id. -- https://developers.circle.com/developer/v1/reference/getwireaccountinstructions getWireAccountInstructions :: UUID -> CircleAPIRequest WireInstructionsRequest TupleBS8 BSL.ByteString getWireAccountInstructions wireAccountId = do mkCircleAPIRequest NHTM.methodGet url params where url = "banks/wires/" <> UUID.toText wireAccountId <> "/instructions" params = Params Nothing [] --------------------------------------------------------------- -- /banks/ach endpoint --------------------------------------------------------------- -- NB: Use createMockACHBankAccount in the sandbox environment create a mock ACH account and retrieve a processor token that can be used to link an ACH account. -- | Create an ACH account -- https://developers.circle.com/developer/v1/reference/payments-bank-accounts-ach-mock createACHAccount :: CreateACHBankAccountRequestBody -> CircleAPIRequest ACHBankAccountRequest TupleBS8 BSL.ByteString createACHAccount achAccountBody = do mkCircleAPIRequest NHTM.methodPost url params where url = "banks/ach" params = Params (Just $ Body (encode achAccountBody)) [] -- | Get an ACH account -- https://developers.circle.com/developer/v1/reference/getachaccount-1 getACHAccount :: UUID -> CircleAPIRequest ACHBankAccountRequest TupleBS8 BSL.ByteString getACHAccount achAccountId = do mkCircleAPIRequest NHTM.methodGet url params where url = "banks/ach/" <> UUID.toText achAccountId params = Params Nothing [] --------------------------------------------------------------- -- /banks/sepa endpoint --------------------------------------------------------------- -- TODO some way to mark this stuff as being in beta? Is it even worth it? -- | Create a SEPA account (in beta) -- https://developers.circle.com/developer/v1/reference/createsepaaccount-1 createSEPAAccount :: SEPAAccountRequestBody -> CircleAPIRequest SEPAAccountRequest TupleBS8 BSL.ByteString createSEPAAccount sepaAccountBody = do mkCircleAPIRequest NHTM.methodPost url params where url = "banks/sepa" params = Params (Just $ Body (encode sepaAccountBody)) [] -- | Get a SEPA account (in beta) -- https://developers.circle.com/developer/v1/reference/getsepaaccount-1 getSEPAAccount :: UUID -> CircleAPIRequest SEPAAccountRequest TupleBS8 BSL.ByteString getSEPAAccount sepaAccountId = do mkCircleAPIRequest NHTM.methodGet url params where url = "banks/sepa/" <> UUID.toText sepaAccountId params = Params Nothing [] -- | Get instructions for a SEPA transfer (in beta) -- https://developers.circle.com/developer/v1/reference/getsepaaccountinstructions getSEPAAccountInstructions :: UUID -> CircleAPIRequest SEPAInstructionsRequest TupleBS8 BSL.ByteString getSEPAAccountInstructions sepaAccountId = do mkCircleAPIRequest NHTM.methodGet url params where url = "banks/sepa/" <> UUID.toText sepaAccountId <> "/instructions" params = Params Nothing [] --------------------------------------------------------------- -- /settlements endpoint --------------------------------------------------------------- -- | List all settlements -- https://developers.circle.com/developer/v1/reference/listsettlements listAllSettlements :: CircleAPIRequest SettlementsRequest TupleBS8 BSL.ByteString listAllSettlements = do mkCircleAPIRequest NHTM.methodGet url params where url = "settlements" params = Params Nothing [] -- | Get a settlement -- https://developers.circle.com/developer/v1/reference/payments-settlements-get-id getSettlement :: UUID -> CircleAPIRequest SettlementRequest TupleBS8 BSL.ByteString getSettlement settlementId = do mkCircleAPIRequest NHTM.methodGet url params where url = "settlements/" <> UUID.toText settlementId params = Params Nothing [] --------------------------------------------------------------- -- /chargebacks endpoint --------------------------------------------------------------- -- | List all chargebacks -- https://developers.circle.com/developer/v1/reference/listchargebacks listAllChargebacks :: CircleAPIRequest ChargebacksRequest TupleBS8 BSL.ByteString listAllChargebacks = do mkCircleAPIRequest NHTM.methodGet url params where url = "chargebacks" params = Params Nothing [] -- | Get a chargeback -- https://developers.circle.com/developer/v1/reference/payments-chargebacks-get-id getChargeback :: UUID -> CircleAPIRequest ChargebackRequest TupleBS8 BSL.ByteString getChargeback chargebackId = do mkCircleAPIRequest NHTM.methodGet url params where url = "chargebacks/" <> UUID.toText chargebackId params = Params Nothing [] --------------------------------------------------------------- -- /reversals endpoint --------------------------------------------------------------- -- | Retrieve a list of ACH payment reversals. Results will be sorted by create date descending; more recent reversals will be at the beginning of the list -- https://developers.circle.com/developer/v1/reference/listreversals listAllACHReversals :: CircleAPIRequest ReversalsRequest TupleBS8 BSL.ByteString listAllACHReversals = do mkCircleAPIRequest NHTM.methodGet url params where url = "reversals" params = Params Nothing [] --------------------------------------------------------------- -- /balances endpoint --------------------------------------------------------------- -- | Retrieves the balance of merchant funds that have settled and also of funds that have been sent for processing but have not yet settled. -- https://developers.circle.com/developer/v1/reference/listbalances listAllBalances :: CircleAPIRequest BalanceRequest TupleBS8 BSL.ByteString listAllBalances = do mkCircleAPIRequest NHTM.methodGet url params where url = "balances" params = Params Nothing [] --------------------------------------------------------------- -- /paymentIntents endpoint --------------------------------------------------------------- -- | List all payment intents -- https://developers.circle.com/developer/v1/reference/listpaymentintents listAllPaymentIntents :: CircleAPIRequest PaymentIntentsRequest TupleBS8 BSL.ByteString listAllPaymentIntents = do mkCircleAPIRequest NHTM.methodGet url params where url = "paymentIntents" params = Params Nothing [] -- | Create a payment intent -- https://developers.circle.com/developer/v1/reference/createpaymentintent createPaymentIntent :: CreatePaymentIntentRequestBody -> CircleAPIRequest PaymentIntentRequest TupleBS8 BSL.ByteString createPaymentIntent createPaymentBody = do mkCircleAPIRequest NHTM.methodPost url params where url = "paymentIntents" params = Params (Just $ Body (encode createPaymentBody)) [] -- | Get a payment intent -- https://developers.circle.com/developer/v1/reference/getpaymentintent getPaymentIntent :: UUID -> CircleAPIRequest PaymentIntentRequest TupleBS8 BSL.ByteString getPaymentIntent paymentIntentId = do mkCircleAPIRequest NHTM.methodGet url params where url = "paymentIntents/" <> UUID.toText paymentIntentId params = Params Nothing [] -- | Expire a payment intent -- https://developers.circle.com/developer/v1/reference/expirepaymentintent expirePaymentIntent :: UUID -> CircleAPIRequest PaymentIntentRequest TupleBS8 BSL.ByteString expirePaymentIntent paymentIntentId = do mkCircleAPIRequest NHTM.methodPost url params where url = "paymentIntents/" <> UUID.toText paymentIntentId <> "/expire" params = Params Nothing [] --------------------------------------------------------------- -- /payouts endpoint --------------------------------------------------------------- -- | Lists all payouts made from a given account -- https://developers.circle.com/developer/v1/reference/listpayouts listAllPayouts :: CircleAPIRequest PayoutsRequest TupleBS8 BSL.ByteString listAllPayouts = do mkCircleAPIRequest NHTM.methodGet url params where url = "payouts" params = Params Nothing [] -- | Gets a specific payout based on an ID -- https://developers.circle.com/developer/v1/reference/payouts-payouts-get-id getPayout :: UUID -> CircleAPIRequest PayoutRequest TupleBS8 BSL.ByteString getPayout payoutId = do mkCircleAPIRequest NHTM.methodGet url params where url = "payouts" <> UUID.toText payoutId params = Params Nothing [] -- | Creates a payout -- https://developers.circle.com/developer/v1/reference/payouts-payouts-create createPayout :: PayoutRequestBody -> CircleAPIRequest PayoutRequest TupleBS8 BSL.ByteString createPayout payoutBody = do mkCircleAPIRequest NHTM.methodPost url params where url = "payouts" params = Params (Just $ Body (encode payoutBody)) [] --------------------------------------------------------------- -- /transfers endpoint (On-chain transfers) --------------------------------------------------------------- -- | Searches for transfers from your account. -- If the date parameters are omitted, returns the most recent transfers. -- This endpoint returns up to 50 transfers in descending chronological order or pageSize, if provided. -- https://developers.circle.com/developer/v1/reference/listtransfers listAllTransfers :: CircleAPIRequest TransfersRequest TupleBS8 BSL.ByteString listAllTransfers = do mkCircleAPIRequest NHTM.methodGet url params where url = "transfers" params = Params Nothing [] -- | Get a transfer based on a transfer ID -- https://developers.circle.com/reference/getbusinesstransfer getTransfer :: UUID -> CircleAPIRequest TransferRequest TupleBS8 BSL.ByteString getTransfer transferId = do mkCircleAPIRequest NHTM.methodGet url params where url = "transfers/" <> UUID.toText transferId params = Params Nothing [] -- | Create a new transfer -- https://developers.circle.com/developer/v1/reference/payouts-transfers-create createTransfer :: TransferRequestBody -> CircleAPIRequest TransferRequest TupleBS8 BSL.ByteString createTransfer transferBody = do mkCircleAPIRequest NHTM.methodPost url params where url = "transfers" params = Params (Just $ Body (encode transferBody)) [] --------------------------------------------------------------- -- /returns endpoint --------------------------------------------------------------- -- | Retrieve a list of Wire and ACH payout returns. Results will be sorted by create date descending; -- more recent returns will be at the beginning of the list. -- https://developers.circle.com/developer/v1/reference/listreturns listAllReturns :: CircleAPIRequest ReturnsRequest TupleBS8 BSL.ByteString listAllReturns = do mkCircleAPIRequest NHTM.methodGet url params where url = "returns" params = Params Nothing [] --------------------------------------------------------------- -- /wallets endpoint --------------------------------------------------------------- -- | Retrieves a list of a user's wallets. -- https://developers.circle.com/developer/v1/reference/listwallets listAllWallets :: CircleAPIRequest WalletsRequest TupleBS8 BSL.ByteString listAllWallets = do mkCircleAPIRequest NHTM.methodGet url params where url = "wallets" params = Params Nothing [] -- | Get a wallet -- https://developers.circle.com/developer/v1/reference/accounts-wallets-get-id getWallet :: UUID -> CircleAPIRequest WalletRequest TupleBS8 BSL.ByteString getWallet walletId = do mkCircleAPIRequest NHTM.methodGet url params where url = "wallets/" <> UUID.toText walletId params = Params Nothing [] -- | Creates an end user wallet. -- https://developers.circle.com/developer/v1/reference/accounts-wallets-create createWallet :: CreateWalletRequestBody -> CircleAPIRequest WalletRequest TupleBS8 BSL.ByteString createWallet walletBody = do mkCircleAPIRequest NHTM.methodPost url params where url = "wallets" params = Params (Just $ Body (encode walletBody)) [] -- | Create new blockchain deposit address -- Generates a new blockchain address for a wallet for a given currency/chain pair. -- Circle may reuse addresses on blockchains that support reuse. -- For example, if you're requesting two addresses for depositing USD and ETH, both on Ethereum, -- you may see the same Ethereum address returned. -- Depositing cryptocurrency to a generated address will credit the associated wallet with the value of the deposit. -- https://developers.circle.com/developer/v1/reference/payments-on-chain-addresses-create createDepositAddress :: UUID -> DepositAddressRequestBody -> CircleAPIRequest DepositAddressRequest TupleBS8 BSL.ByteString createDepositAddress walletId depositAddressBody = do mkCircleAPIRequest NHTM.methodPost url params where url = "wallets/" <> UUID.toText walletId <> "/addresses" params = Params (Just $ Body (encode depositAddressBody)) [] -- | List all recipient addresses associated with a wallet Id -- Retrieves a list of addresses associated with a wallet. -- https://developers.circle.com/developer/v1/reference/listaddresses listAllAddresses :: UUID -> CircleAPIRequest RecipientAddressesRequest TupleBS8 BSL.ByteString listAllAddresses walletId = do mkCircleAPIRequest NHTM.methodGet url params where url = "wallets/" <> UUID.toText walletId <> "/addresses" params = Params Nothing [] --------------------------------------------------------------- -- Utility methods for calling Circle's API --------------------------------------------------------------- -- | Create a request to `circle`'s API circle :: (FromJSON (CircleRequest a)) => CircleConfig -> CircleAPIRequest a TupleBS8 BSL.ByteString -> IO (Either CircleError (CircleRequest a)) circle config request = do response <- circle' config request let result = eitherDecode $ responseBody response case result of Left s -> return (Left (CircleError (T.pack s) response)) Right r -> return (Right r) where circle' :: CircleConfig -> CircleAPIRequest a TupleBS8 BSL.ByteString -> IO Reply circle' CircleConfig {..} CircleAPIRequest {..} = do manager <- newManager tlsManagerSettings initReq <- parseRequest $ T.unpack $ T.append (hostUri host) endpoint let reqBody | rMethod == NHTM.methodGet = mempty | isNothing (paramsBody params) = mempty | otherwise = unBody $ fromJust $ paramsBody params req = initReq { method = rMethod, requestBody = RequestBodyLBS reqBody, requestHeaders = [(hContentType, "application/json"), (hAccept, "application/json")], queryString = paramsToByteString $ paramsQuery params } circleToken = unApiToken token authorizedRequest = applyBearerAuth circleToken req httpLbs authorizedRequest manager -- | This function is only used internally to speed up the test suite. -- Instead of creating a new Manager we reuse the same one. circleTest :: (FromJSON (CircleRequest a)) => CircleConfig -> Manager -> CircleAPIRequest a TupleBS8 BSL.ByteString -> IO (Either CircleError (CircleRequest a)) circleTest config tlsManager request = do response <- circleTest' config request tlsManager let result = eitherDecode $ responseBody response case result of Left s -> return (Left (CircleError (T.pack s) response)) Right r -> return (Right r) where circleTest' :: CircleConfig -> CircleAPIRequest a TupleBS8 BSL.ByteString -> Manager -> IO Reply circleTest' CircleConfig {..} CircleAPIRequest {..} manager = do initReq <- parseRequest $ T.unpack $ T.append (hostUri host) endpoint let reqBody | rMethod == NHTM.methodGet = mempty | isNothing (paramsBody params) = mempty | otherwise = unBody $ fromJust $ paramsBody params req = initReq { method = rMethod, requestBody = RequestBodyLBS reqBody, requestHeaders = [(hContentType, "application/json"), (hAccept, "application/json")], queryString = paramsToByteString $ paramsQuery params } circleToken = unApiToken token authorizedRequest = applyBearerAuth circleToken req httpLbs authorizedRequest manager -- | Conversion of a key value pair to a query parameterized string paramsToByteString :: [Query] -> BS8.ByteString paramsToByteString [] = mempty paramsToByteString [x] = fst (unQuery x) <> "=" <> snd (unQuery x) paramsToByteString (x : xs) = mconcat [fst $ unQuery x, "=", snd $ unQuery x, "&"] <> paramsToByteString xs