Accepting Payments

Yleiskatsaus

Fenerum integroituu johtaviin maksupalveluntarjoajiin auttaakseen sinua keräämään ja hallitsemaan asiakkaidesi maksutapoja. Kun maksukortti on rekisteröity Fenerumiin, voit veloittaa sen automaattisesti laskuista ja tilauksista.

Tuetut palveluntarjoajat:

Moderni maksuinfrastruktuuri

Pohjoismainen maksuvälityspalvelu

Edellytykset

Ennen kuin alat vastaanottaa maksuja, määritä maksupalveluntarjoaja Fenerumissa:

  1. Siirry Fenerumissa kohtaan SettingsIntegrations
  2. Lisää maksupalveluntarjoajasi API‑tunnistetiedot
  3. Ota maksuyhdyskäytävä käyttöön organisaatiollesi

Stripe Integration

Näin se toimii

Stripen integrointi Fenerumiin koostuu kahdesta vaiheesta:

  1. Frontend: Kerää korttitiedot Stripe Elementsin avulla (PCI‑yhteensopiva, korttidata menee suoraan Stripelle)
  2. Backend: Rekisteröi Stripen payment method ID Fenerumiin (palvelimellasi, turvallinen token‑käsittely)

Tietoturvahuomio: Älä koskaan paljasta Fenerumin API‑tokenia frontend‑koodissa. Kutsu Fenerumin APIa aina backendistä, jossa token on turvallisesti tallennettu ympäristömuuttujiin.

Frontend: kerää maksutapa

Käytä Stripe Elementsia korttitietojen turvalliseen keräämiseen. Korttidata lähetetään suoraan Stripelle — palvelimesi ei näe sitä.

<!DOCTYPE html>
<html>
<head>
  <title>Add Payment Method</title>
  <script src="https://js.stripe.com/v3/"></script>
  <style>
    #payment-form { max-width: 500px; margin: 50px auto; padding: 20px; }
    #card-element { border: 1px solid #ccc; padding: 10px; border-radius: 4px; }
    button { margin-top: 20px; padding: 10px 20px; background: #5469d4; color: white; border: none; border-radius: 4px; cursor: pointer; }
    button:hover { background: #4158c4; }
    .success { color: green; margin-top: 20px; }
    .error { color: red; margin-top: 20px; }
  </style>
</head>
<body>
  <form id="payment-form">
    <h2>Add Payment Method</h2>
    <div id="card-element"></div>
    <button type="submit">Save Card</button>
    <div id="message"></div>
  </form>

  <script>
    // Initialize Stripe with your publishable key (safe for frontend)
    const stripe = Stripe('pk_test_YOUR_PUBLISHABLE_KEY')
    const elements = stripe.elements()
    const cardElement = elements.create('card')
    cardElement.mount('#card-element')

    const form = document.getElementById('payment-form')
    const messageDiv = document.getElementById('message')

    form.addEventListener('submit', async (event) => {
      event.preventDefault()
      messageDiv.textContent = ''

      // Step 1: Create payment method with Stripe (card data stays secure)
      const {error, paymentMethod} = await stripe.createPaymentMethod({
        type: 'card',
        card: cardElement,
      })

      if (error) {
        messageDiv.textContent = error.message
        messageDiv.className = 'error'
        return
      }

      // Step 2: Send ONLY the payment method ID to YOUR backend
      <!-- focus-start -->
      try {
        const response = await fetch('/api/register-payment-card', {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({
            paymentMethodId: paymentMethod.id,
            accountId: 'ACCOUNT_UUID_HERE'
          })
        })

        const data = await response.json()
      <!-- focus-end -->

        if (response.ok) {
          messageDiv.textContent = `Card saved: ${data.brand} ending in ${data.card_number.slice(-4)}`
          messageDiv.className = 'success'
          form.reset()
        } else {
          messageDiv.textContent = data.error || 'Failed to save card'
          messageDiv.className = 'error'
        }
      } catch (err) {
        messageDiv.textContent = 'Network error: ' + err.message
        messageDiv.className = 'error'
      }
    })
  </script>
</body>
</html>

Keskeiset huomiot:

  • Stripen publishable key (pk_test_...) on turvallista käyttää frontend‑koodissa
  • Korttidata lähetetään suoraan Stripelle, eikä se kulje palvelimesi kautta
  • Vain payment method ID lähetetään backendiin
  • Ei Fenerumin API‑tokenia frontendiin!

Backend: rekisteröi Fenerumiin

Backend vastaanottaa Stripen payment method ID:n ja rekisteröi sen Fenerumiin käyttäen ympäristömuuttujiin tallennettua API‑tokenia.

// Example backend endpoint (works with Express, Fastify, Next.js, etc.)
// Endpoint: POST /api/register-payment-card
// Body: { paymentMethodId: string, accountId: string }

async function registerPaymentCard(req, res) {
  const { paymentMethodId, accountId } = req.body

  // Validate inputs
  if (!paymentMethodId || !accountId) {
    return res.status(400).json({ error: 'Missing required fields' })
  }

  <!-- focus-start -->
  try {
    // Call Fenerum API with token from environment variables
    const response = await fetch('https://app.fenerum.com/api/v1/paymentcards/', {
      method: 'POST',
      headers: {
        'Authorization': `Token ${process.env.FENERUM_API_TOKEN}`,
        'X-Client-System': 'YourApp',
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        account: accountId,
        token: paymentMethodId,
        gateway: 'stripe_new'
      })
    })

    const data = await response.json()
  <!-- focus-end -->

    if (!response.ok) {
      console.error('Fenerum API error:', data)
      return res.status(response.status).json({
        error: 'Failed to register card with Fenerum'
      })
    }

    // Return success with card details
    return res.status(200).json({
      uuid: data.uuid,
      brand: data.brand,
      card_number: data.card_number,
      month: data.month,
      year: data.year
    })
  } catch (error) {
    console.error('Error registering payment card:', error)
    return res.status(500).json({ error: 'Internal server error' })
  }
}

Ympäristömuuttujien määrittäminen:

# .env file (NEVER commit this to version control!)
FENERUM_API_TOKEN=your_actual_token_here

Esimerkkivastaus:

{
  "uuid": "09655a26-12e0-4a82-8524-7851998886fc",
  "brand": "Visa",
  "card_number": "XXXXXXXXXXXX4242",
  "month": 12,
  "year": 2025
}

Customer Self-Service

Suositus: käytä Fenerum Self-Service -palvelua

Paras tapa antaa asiakkaiden hallita maksutapojaan on Fenerum Self-Service — täysin hostattu portaali, jossa asiakkaat voivat lisätä maksukortteja, hallita tilauksia ja tarkastella laskuja ilman koodausta.

Hyödyt:

  • Asiakkaat voivat lisätä ja hallita maksukortteja itse Stripen tai Quickpayn kautta
  • Automaattinen integraatio brändisi kanssa
  • Ei koodia — ota vain käyttöön organisaatiosi asetuksissa
  • Turvallinen autentikointi sähköpostilinkin kautta

API‑integraatio:

Luo asiakkaille suora käyttölinkki Self‑Serviceen:

# Generate Self-Service access link
curl -X POST https://app.fenerum.com/api/self-service/initiate-organization/ \
  -H "Authorization: Token YOUR_FENERUM_TOKEN" \
  -H "X-Client-System: YourApp" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "customer@example.com"
  }'

Vastaus:

{
  "url": "https://your-company.hostedsignup.com/access/abc123..."
}

Lähetä tämä linkki asiakkaallesi — hän pääsee välittömästi omaan portaaliinsa lisäämään maksukortteja ja hallitsemaan tilauksiaan.

Kun asiakkaat lisäävät maksukortin Self‑Servicen kautta, Fenerum:

  • Tallentaa kortin asiakkaan tilille
  • Asettaa “Payment card” oletusperintätavaksi
  • Päivittää kaikki aktiiviset tilaukset käyttämään uutta maksutapaa

Lue lisää: Self‑Service Documentation

Quickpay Integration

Näin se toimii

Fenerum käyttää QuickPay subscriptions -toiminnallisuutta maksukorttien tallentamiseen. Integraatiopolku on seuraava:

  1. Backend: Luo QuickPay‑maksulinkki QuickPayn APIlla
  2. Frontend: Näytä asiakkaalle QuickPayn maksulomake (korttidata menee suoraan QuickPaylle)
  3. Callback: QuickPay lähettää callbackin, kun subscription ID on muodostettu
  4. Backend: Rekisteröi subscription ID Fenerumiin

Tietoturvahuomio: Kaikki API‑kutsut (sekä QuickPay että Fenerum) on tehtävä backendissä, jossa tokenit säilytetään turvallisesti.

Vaihe 1: Luo QuickPay-maksulinkki

Backendi luo QuickPay‑maksulinkin käyttäen QuickPayn payment link APIa.

// Backend endpoint to create QuickPay payment link
// Endpoint: POST /api/create-quickpay-link
// Body: { accountId: string, customerEmail: string }

async function createQuickpayLink(req, res) {
  const { accountId, customerEmail } = req.body

  <!-- focus-start -->
  try {
    // Create QuickPay payment (subscription)
    const paymentResponse = await fetch('https://api.quickpay.net/payments', {
      method: 'POST',
      headers: {
        'Authorization': `Basic ${Buffer.from(':' + process.env.QUICKPAY_API_KEY).toString('base64')}`,
        'Accept-Version': 'v10',
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        currency: 'DKK',
        order_id: `sub_${accountId}_${Date.now()}`,
        variables: {
          fenerum_account_id: accountId,
          customer_email: customerEmail
        }
      })
    })

    const payment = await paymentResponse.json()

    // Create payment link for the subscription
    const linkResponse = await fetch(`https://api.quickpay.net/payments/${payment.id}/link`, {
      method: 'PUT',
      headers: {
        'Authorization': `Basic ${Buffer.from(':' + process.env.QUICKPAY_API_KEY).toString('base64')}`,
        'Accept-Version': 'v10',
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        amount: 0, // 0 for card authorization only
        auto_capture: false,
        callback_url: `${process.env.YOUR_DOMAIN}/api/quickpay-callback`
      })
    })

    const link = await linkResponse.json()
  <!-- focus-end -->

    return res.json({ url: link.url })
  } catch (error) {
    console.error('Error creating QuickPay link:', error)
    return res.status(500).json({ error: 'Internal server error' })
  }
}

Ympäristöasetukset:

QUICKPAY_API_KEY=your_quickpay_api_key_here
YOUR_DOMAIN=https://yourdomain.com
FENERUM_API_TOKEN=your_fenerum_token_here

Vaihe 2: Uudelleenohjausreitti

Luo backend‑reitti, joka generoi QuickPay‑linkin ja uudelleenohjaa asiakkaan suoraan QuickPayn hostatulle sivulle.

// Backend redirect route
// When customer visits: GET /add-payment-card
// They are redirected to QuickPay's hosted payment page

async function redirectToQuickpay(req, res) {
  // Get logged-in user's information from session/auth
  const accountId = req.user.fenerumAccountId
  const email = req.user.email

  <!-- focus-start -->
  try {
    // Create QuickPay payment (subscription)
    const paymentResponse = await fetch('https://api.quickpay.net/payments', {
      method: 'POST',
      headers: {
        'Authorization': `Basic ${Buffer.from(':' + process.env.QUICKPAY_API_KEY).toString('base64')}`,
        'Accept-Version': 'v10',
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        currency: 'DKK',
        order_id: `sub_${accountId}_${Date.now()}`,
        variables: {
          fenerum_account_id: accountId,
          customer_email: email
        }
      })
    })

    const payment = await paymentResponse.json()

    // Create payment link
    const linkResponse = await fetch(`https://api.quickpay.net/payments/${payment.id}/link`, {
      method: 'PUT',
      headers: {
        'Authorization': `Basic ${Buffer.from(':' + process.env.QUICKPAY_API_KEY).toString('base64')}`,
        'Accept-Version': 'v10',
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        amount: 0,
        auto_capture: false,
        callback_url: `${process.env.YOUR_DOMAIN}/api/quickpay-callback`
      })
    })

    const link = await linkResponse.json()

    // Redirect customer to QuickPay
    return res.redirect(link.url)
  <!-- focus-end -->
  } catch (error) {
    console.error('Error creating QuickPay link:', error)
    return res.status(500).send('Failed to create payment link')
  }
}

Käyttö:

Anna asiakkaille linkki backend‑reitille:

<a href="/add-payment-card">Add Payment Method</a>

Reitti käyttää sisäänkirjautuneen käyttäjän sessiosta/authista saatavia tietoja tilin ID:stä ja sähköpostista — URLissa ei ole arkaluonteista dataa.

Vaihe 3: Käsittele QuickPay-callback

Kun asiakas on täyttänyt maksulomakkeen, QuickPay lähettää palvelimellesi callbackin, joka sisältää subscription ID:n.

// QuickPay callback handler
// Endpoint: POST /api/quickpay-callback
// Receives QuickPay callback when card is added

async function handleQuickpayCallback(req, res) {
  const { id: subscriptionId, variables } = req.body
  const accountId = variables?.fenerum_account_id

  if (!subscriptionId || !accountId) {
    return res.status(400).json({ error: 'Missing required data' })
  }

  <!-- focus-start -->
  try {
    // Register the QuickPay subscription with Fenerum
    const response = await fetch('https://app.fenerum.com/api/v1/paymentcards/', {
      method: 'POST',
      headers: {
        'Authorization': `Token ${process.env.FENERUM_API_TOKEN}`,
        'X-Client-System': 'YourApp',
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        account: accountId,
        token: subscriptionId.toString(), // QuickPay subscription ID
        gateway: 'quickpay'
      })
    })

    const data = await response.json()
  <!-- focus-end -->

    if (response.ok) {
      console.log('Payment card registered with Fenerum:', data)
      return res.status(200).json({ success: true })
    } else {
      console.error('Failed to register card with Fenerum:', data)
      return res.status(500).json({ error: 'Failed to register card' })
    }
  } catch (error) {
    console.error('Error in QuickPay callback:', error)
    return res.status(500).json({ error: 'Internal server error' })
  }
}

Fenerumin vastaus:

{
  "uuid": "09655a26-12e0-4a82-8524-7851998886fc",
  "brand": "Visa",
  "card_number": "XXXXXXXXXXXX4242",
  "month": 12,
  "year": 2025,
  "payment_gateway": "quickpay",
  "account": "50e9aa0b-8d35-4da7-9a9b-8a35b11579b2"
}

Keskeiset huomiot:

  • QuickPayn subscription ID on tallennettava Fenerumiin merkkijonona
  • gateway‑arvon on oltava quickpay
  • Korttidata ei koskaan kulje palvelimesi kautta — se menee suoraan QuickPaylle
  • Katso QuickPay integration guide asennusohjeita varten

Customer Self-Service

Suositus: käytä Fenerum Self-Service -palvelua

Paras tapa on käyttää Fenerum Self-Service ‑portaalia, jossa asiakkaat voivat lisätä QuickPay‑maksukortit itse hostatussa käyttöliittymässä.

Self‑Service‑portaali tukee sekä Stripe‑ että Quickpay‑maksutapoja ja tarjoaa yhtenäisen käyttökokemuksen asiakkaillesi. Katso API‑integraation tiedot yllä olevasta [Customer Self-Service]#customer-self-service ‑osiosta Stripen kohdalta.

Maksukorttien hallinta

Listaa tilin kortit

curl https://app.fenerum.com/api/v1/paymentcards/?account=CUST001 \
  -H "Authorization: Token YOUR_FENERUM_TOKEN" \
  -H "X-Client-System: YourApp"

Vastaus:

{
  "count": 2,
  "next": null,
  "previous": null,
  "results": [
    {
      "uuid": "09655a26-12e0-4a82-8524-7851998886fc",
      "active": true,
      "brand": "Visa",
      "card_number": "XXXXXXXXXXXX4242",
      "month": 12,
      "year": 2025,
      "payment_gateway": "stripe_new",
      "account": "50e9aa0b-8d35-4da7-9a9b-8a35b11579b2"
    }
  ]
}

Hae kortin tiedot

curl https://app.fenerum.com/api/v1/paymentcards/{card_uuid}/ \
  -H "Authorization: Token YOUR_FENERUM_TOKEN" \
  -H "X-Client-System: YourApp"

Poista kortti käytöstä

curl -X POST https://app.fenerum.com/api/v1/paymentcards/{card_uuid}/disable/ \
  -H "Authorization: Token YOUR_FENERUM_TOKEN" \
  -H "X-Client-System: YourApp"

Huomio: Kortin poistaminen käytöstä Fenerumissa ei poista sitä maksupalveluntarjoajalta. Se ainoastaan estää Fenerumia veloittamasta korttia.

Maksuyhdyskäytävän viitetiedot

Kun luot maksukortteja, käytä seuraavia gateway‑tunnisteita:

PalveluntarjoajaGateway‑arvoToken-tyyppi
Stripestripe_newPayment Method ID (pm_...)
QuickpayquickpaySubscription ID (integer)

Vianmääritys

Kortin rekisteröinti epäonnistuu:

  • Varmista, että maksupalveluntarjoajan tunnukset on määritetty oikein Fenerumin asetuksissa
  • Tarkista, että tilin UUID on olemassa Fenerumissa
  • Varmista, että tokenin muoto vastaa gateway‑tyyppiä (pm_... Stripelle, integer Quickpaylle)
  • Vahvista, että käytät oikeaa gateway‑arvoa (stripe_new tai quickpay)
Previous
Logging
background logo

We invoice 2 billion DKK annually for our customers. Let's put your invoicing on autopilot today!