Noorem Tarkvaraarendaja eriala

Veebirakenduste loomine API päringute abil

  1. Project creation

1.1. Ava Visual Studio

1.2. Loo uus projekt

1.3. Vali ASP.NET Core Web API

1.4. Nimeta see, kuidas soovid

1.5. Sisesta need vanad seaded

  1. ASP.NET API-ga tutvumine

2.1. Paremklõpsa kaustal Controllers, vali Add -> Controller…

2.2. Vali API Controller – Empty, nimeta see PrimitiividController.cs

2.3. Kirjuta see kood:

C#
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;

namespace Veeb_TARpv23.Controllers
{
    [Route("[controller]")]
    [ApiController]
    public class PrimitiividController : ControllerBase
    {

        // GET: primitiivid/hello-world
        [HttpGet("hello-world")]
        public string HelloWorld()
        {
            return "Hello world at " + DateTime.Now;
        }

    }
}

2.4. Käivita see, mine oma localhost:[port]/Primitiivid/hello-world URL-ile

2.5. Kirjuta see kood:

C#
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;

namespace Veeb_TARpv23.Controllers
{
    [Route("[controller]")]
    [ApiController]
    public class PrimitiividController : ControllerBase
    {

        // GET: primitiivid/hello-world
        [HttpGet("hello-world")]
        public string HelloWorld()
        {
            return "Hello world at " + DateTime.Now;
        }

        // GET: primitiivid/hello-variable/mari
        [HttpGet("hello-variable/{nimi}")]
        public string HelloVariable(string nimi)
        {
            return "Hello " + nimi;
        }

        // GET: primitiivid/add/5/6
        [HttpGet("add/{nr1}/{nr2}")]
        public int AddNumbers(int nr1, int nr2)
        {
            return nr1 + nr2;
        }

        // GET: primitiivid/multiply/5/6
        [HttpGet("multiply/{nr1}/{nr2}")]
        public int Multiply(int nr1, int nr2)
        {
            return nr1 * nr2;
        }

        // GET: primitiivid/do-logs/5
        [HttpGet("do-logs/{arv}")]
        public void DoLogs(int arv)
        {
            for (int i = 0; i < arv; i++)
            {
                Console.WriteLine("See on logi nr " + i);
            }
        }
    }
}

2.6. Testi endpoint’e:

2.6.1. Mine localhost:[port]/Primitiivid/hello-variable/[NIMI] URL-ile

2.6.2. Mine localhost:[port]/Primitiivid/add/[NUMBER 1]/[NUMBER 2] URL-ile

2.6.3. Mine localhost:[port]/Primitiivid/multiply/[NUMBER 1]/[NUMBER 2] URL-ile

2.6.3. Mine localhost:[port]/Primitiivid/do-logs/[KOGUS] URL-ile ja kontrolli konsooli

2.7. Loo uus kaust nimega Models

2.8. Loo uude kausta uus C# klass nimega Toode.cs

2.9. Kirjuta sisse see kood:

C#
namespace Veeb_TARpv23.Models
{
    public class Toode
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public double Price { get; set; }
        public bool IsActive { get; set; }

        public Toode(int id, string name, double price, bool isActive)
        {
            Id = id;
            Name = name;
            Price = price;
            IsActive = isActive;
        }
    }
}

2.10. Loo uus Controller ja nimeta see ToodeController.cs

2.11. Kirjuta sisse see kood:

C#
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Veeb_TARpv23.Models;

namespace Veeb_TARpv23.Controllers
{
    [Route("[controller]")]
    [ApiController]
    public class ToodeController : ControllerBase
    {
        private static Toode _toode = new Toode(1, "Koola", 1.5, true);

        // GET: toode
        [HttpGet]
        public Toode GetToode()
        {
            return _toode;
        }

        // GET: toode/suurenda-hinda
        [HttpGet("suurenda-hinda")]
        public Toode SuurendaHinda()
        {
            _toode.Price = _toode.Price + 1;
            return _toode;
        }
    }
}

2.12. Mine testimiseks localhost:[port]/Toode

2.13. Mine localhost:[port]/Toode/suurenda-hinda, et näha, kas hind muutub

2.14. Loo uus Controller ja nimeta see TootedController.cs, kirjuta sisse see kood:

C#
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Veeb_TARpv23.Models;

namespace Veeb_TARpv23.Controllers
{
    [ApiController]
    [Route("[controller]")]
    public class TootedController : ControllerBase
    {
        private static List<Toode> _tooted = new List<Toode>{
        new Toode(1,"Koola", 1.5, true),
        new Toode(2,"Fanta", 1.0, false),
        new Toode(3,"Sprite", 1.7, true),
        new Toode(4,"Vichy", 2.0, true),
        new Toode(5,"Vitamin well", 2.5, true)
        };


        // https://localhost:7052/tooted
        [HttpGet]
        public List<Toode> Get()
        {
            return _tooted;
        }

        [HttpGet("kustuta/{index}")]
        public List<Toode> Delete(int index)
        {
            _tooted.RemoveAt(index);
            return _tooted;
        }

        [HttpGet("kustuta2/{index}")]
        public string Delete2(int index)
        {
            _tooted.RemoveAt(index);
            return "Kustutatud!";
        }

        [HttpGet("lisa/{id}/{nimi}/{hind}/{aktiivne}")]
        public List<Toode> Add(int id, string nimi, double hind, bool aktiivne)
        {
            Toode toode = new Toode(id, nimi, hind, aktiivne);
            _tooted.Add(toode);
            return _tooted;
        }

        [HttpGet("lisa")] // GET /tooted/lisa?id=1&nimi=Koola&hind=1.5&aktiivne=true
        public List<Toode> Add2([FromQuery] int id, [FromQuery] string nimi, [FromQuery] double hind, [FromQuery] bool aktiivne)
        {
            Toode toode = new Toode(id, nimi, hind, aktiivne);
            _tooted.Add(toode);
            return _tooted;
        }

        [HttpGet("hind-dollaritesse/{kurss}")] // GET /tooted/hind-dollaritesse/1.5
        public List<Toode> Dollaritesse(double kurss)
        {
            for (int i = 0; i < _tooted.Count; i++)
            {
                _tooted[i].Price = _tooted[i].Price * kurss;
            }
            return _tooted;
        }

        // või foreachina:

        [HttpGet("hind-dollaritesse2/{kurss}")] // GET /tooted/hind-dollaritesse2/1.5
        public List<Toode> Dollaritesse2(double kurss)
        {
            foreach (var t in _tooted)
            {
                t.Price = t.Price * kurss;
            }

            return _tooted;
        }
    }
}

2.15. Testi endpoint’e:

2.15.1. localhost:[port]/Tooted

2.15.2. localhost:[port]/Tooted/kustuta/[ID]

2.15.3. localhost:[port]/Tooted/kustuta2/[ID]

2.15.4. localhost:[port]/Tooted/lisa/[ID]/[Nimi]/[Hind]/[Aktiivne]

2.15.5. localhost:[port]/Tooted/lisa?id=[ID]&nimi=[Nimi]&hind=[Hind]&aktiivne=[Aktiivne]

2.15.6. localhost:[port]/Tooted/hind-dollaritesse/[Kurss]

2.15.7. localhost:[port]/Tooted/hind-dollaritesse2/[Kurss]

  1. Frontend

3.1. Loo eraldi projekt nimega frontend, kasutades käsku npx create-react-app frontend, veendu, et npm on installitud

3.2. Et testida, kas projekt loodi edukalt, mine käsuga cd projekti kausta ja sisesta npm start.

3.3. Sisesta see kood ja vaata, kas see töötab. App.js:

JavaScript
import { useEffect, useRef, useState } from 'react';
import './App.css';

function App() {
  const [tooted, setTooted] = useState([]);

  useEffect(() => {
    fetch("https://localhost:4444/tooted")
      .then(res => res.json())
      .then(json => setTooted(json));
  }, []);

  return (
    <div className="App">
      {tooted.map(toode => 
        <div>
          <div>{toode.id}</div>
          <div>{toode.name}</div>
          <div>{toode.price.toFixed(2)}</div>
        </div>)}
    </div>
  );
}

export default App;

3.3.1. Veendu, et õige port on kirjutatud faili launchSettings.json:

JSON
{
  "$schema": "https://json.schemastore.org/launchsettings.json",
  "iisSettings": {
    "windowsAuthentication": false,
    "anonymousAuthentication": true,
    "iisExpress": {
      "applicationUrl": "http://localhost:53941",
      "sslPort": 44380
    }
  },
  "profiles": {
    "Veeb_TARpv23": {
      "commandName": "Project",
      "dotnetRunMessages": true,
      "launchBrowser": true,
      "launchUrl": "swagger",
      "applicationUrl": "https://localhost:4444;http://localhost:5028",
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      }
    },
    "IIS Express": {
      "commandName": "IISExpress",
      "launchBrowser": true,
      "launchUrl": "swagger",
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      }
    }
  }
}

3.3.2. Kasuta kindlasti CORS-i, et vältida probleeme. Seda saad teha, muutes faili Program.cs järgmiselt:

C#
var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();

app.UseCors(options => options
    .WithOrigins("*")
    .AllowAnyMethod()
    .AllowAnyHeader()
);

app.UseAuthorization();

app.MapControllers();

app.Run();

3.4. Uuenda koodi uue funktsionaalsuse lisamiseks. App.js:

JavaScript
import { useEffect, useRef, useState } from 'react';
import './App.css';

function App() {
  const [tooted, setTooted] = useState([]);
  const idRef = useRef();
  const nameRef = useRef();
  const priceRef = useRef();
  const isActiveRef = useRef();
  const [isUsd, setUsd] = useState(false);

  useEffect(() => {
    fetch("https://localhost:4444/tooted")
      .then(res => res.json())
      .then(json => setTooted(json));
  }, []);

  function kustuta(index) {
    fetch("https://localhost:4444/tooted/kustuta/" + index)
      .then(res => res.json())
      .then(json => setTooted(json));
  }

  function lisa() {
    fetch(`https://localhost:4444/tooted/lisa/${Number(idRef.current.value)}/${nameRef.current.value}/${Number(priceRef.current.value)}/${isActiveRef.current.checked}`)
      .then(res => res.json())
      .then(json => setTooted(json));
  }

  function dollariteks() {
    const kurss = 1.1;
    setUsd(true);
    fetch("https://localhost:4444/tooted/hind-dollaritesse/" + kurss)
      .then(res => res.json())
      .then(json => setTooted(json));
  }

  function eurodeks() {
    const kurss = 0.9091;
    setUsd(false);
    fetch("https://localhost:4444/tooted/hind-dollaritesse/" + kurss)
      .then(res => res.json())
      .then(json => setTooted(json));
  }

  return (
    <div className="App">
      <label>ID</label> <br />
      <input ref={idRef} type="number" /> <br />
      <label>name</label> <br />
      <input ref={nameRef} type="text" /> <br />
      <label>price</label> <br />
      <input ref={priceRef} type="number" /> <br />
      <label>isActive</label> <br />
      <input ref={isActiveRef} type="checkbox" /> <br />
      <button onClick={() => lisa()}>Lisa</button>
      {tooted.map((toode, index) => 
        <div>
          <div>{toode.id}</div>
          <div>{toode.name}</div>
          <div>{toode.price.toFixed(2)}</div>
          <button onClick={() => kustuta(index)}>x</button>
        </div>)}
      {isUsd === false && <button onClick={() => dollariteks()}>Muuda dollariteks</button>}
      {isUsd === true && <button onClick={() => eurodeks()}>Muuda eurodeks</button>}
    </div>
  );
}

export default App;

3.5. Nüüd veendume, et CORS mõjutab ainult porti 3000:

C#
var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();

app.UseCors(options => options
    .WithOrigins("http://localhost:3000")
    .AllowAnyMethod()
    .AllowAnyHeader()
);

app.UseAuthorization();

app.MapControllers();

app.Run();

3.6. Teeme oma meetodid korrektseks, asendades POST-i teiste meetoditega, TootedController.cs:

C#
using Microsoft.AspNetCore.Mvc;
using Veeb_TARpv23.Models;

namespace Veeb_TARpv23.Controllers
{
    [Route("[controller]")]
    [ApiController]
    public class TootedController : ControllerBase
    {
        private static List<Toode> _tooted = new()
        {
            new Toode(1,"Koola", 1.5, true),
            new Toode(2,"Fanta", 1.0, false),
            new Toode(3,"Sprite", 1.7, true),
            new Toode(4,"Vichy", 2.0, true),
            new Toode(5,"Vitamin well", 2.5, true)
        };

        // GET https://localhost:4444/tooted
        [HttpGet]
        public List<Toode> Get()
        {
            return _tooted;
        }

        // DELETE https://localhost:4444/tooted/kustuta/0
        [HttpDelete("kustuta/{index}")]
        public List<Toode> Delete(int index)
        {
            _tooted.RemoveAt(index);
            return _tooted;
        }

        [HttpDelete("kustuta2/{index}")]
        public string Delete2(int index)
        {
            _tooted.RemoveAt(index);
            return "Kustutatud!";
        }

        [HttpPost("lisa")]
        public List<Toode> Add([FromBody] Toode toode)
        {
            _tooted.Add(toode);
            return _tooted;
        }

        // POST https://localhost:4444/tooted/lisa/1/Coca/1.5/true
        [HttpPost("lisa/{id}/{nimi}/{hind}/{aktiivne}")]
        public List<Toode> Add(int id, string nimi, double hind, bool aktiivne)
        {
            Toode toode = new Toode(id, nimi, hind, aktiivne);
            _tooted.Add(toode);
            return _tooted;
        }

        [HttpPost("lisa2")]
        public List<Toode> Add2(int id, string nimi, double hind, bool aktiivne)
        {
            Toode toode = new Toode(id, nimi, hind, aktiivne);
            _tooted.Add(toode);
            return _tooted;
        }

        // PATCH https://localhost:4444/tooted/hind-dollaritesse/1.5
        [HttpPatch("hind-dollaritesse/{kurss}")] 
        public List<Toode> UpdatePrices(double kurss)
        {
            for (int i = 0; i < _tooted.Count; i++)
            {
                _tooted[i].Price = _tooted[i].Price * kurss;
            }
            return _tooted;
        }
    }
}

App.js:

JavaScript
import { useEffect, useRef, useState } from 'react';
import './App.css';

function App() {
  const [tooted, setTooted] = useState([]);
  const idRef = useRef();
  const nameRef = useRef();
  const priceRef = useRef();
  const isActiveRef = useRef();


  useEffect(() => {
    fetch("https://localhost:4444/tooted")
      .then(res => res.json())
      .then(json => setTooted(json));
  }, []);

  function kustuta(index) {
    fetch("https://localhost:4444/tooted/kustuta/" + index, {"method": "DELETE"})
      .then(res => res.json())
      .then(json => setTooted(json));
  }

  ////////////////////////
  function lisa() {
    const uusToode = {
      "id": Number(idRef.current.value),
      "name": nameRef.current.value,
      "price": Number(priceRef.current.value),
      "isActive": isActiveRef.current.checked
    }
    fetch("https://localhost:4444/tooted/lisa", {"method": "POST", "body": JSON.stringify(uusToode)})
      .then(res => res.json())
      .then(json => setTooted(json));
  }
  ////////////////////////

  function dollariteks() {
    const kurss = 1.1;
    fetch("https://localhost:4444/tooted/hind-dollaritesse/" + kurss, {"method": "PATCH"})
      .then(res => res.json())
      .then(json => setTooted(json));
  }

  return (
    <div className="App">
      <label>ID</label> <br />
      <input ref={idRef} type="number" /> <br />
      <label>Nimi</label> <br />
      <input ref={nameRef} type="text" /> <br />
      <label>Hind</label> <br />
      <input ref={priceRef} type="number" /> <br />
      <label>Aktiivne</label> <br />
      <input ref={isActiveRef} type="checkbox" /> <br />
      <button onClick={() => lisa()}>Lisa</button>
      {tooted.map((toode, index) => 
        <div>
          <div>{toode.id}</div>
          <div>{toode.name}</div>
          <div>{toode.price}</div>
          <button onClick={() => kustuta(index)}>x</button>
        </div>)}
      <button onClick={() => dollariteks()}>Muuda dollariteks</button>
    </div>
  );
}

export default App;

3.7. Nüüd proovime Postmani abil saata POST-päringut. Kasutame VSCode’i laiendust, läheme selle vahekaardile, vajutame “New HTTP request“, kirjutame väljale https://localhost:4444/tooted/lisa, valime ‘body’, vajutame ‘raw’, muudame vormingu JSON-iks ja kirjutame:

JSON
{
  "id": 321,
  "name": "Red bull",
  "price": 5,
  "isActive": true
}
  1. API requests

4.1. Omniva

4.1.1. Loome uue kontrolleri nimega ParcelMachineController.cs:

C#
using Microsoft.AspNetCore.Mvc;

namespace Veeb_TARpv23.Controllers
{
    [Route("[controller]")]
    [ApiController]
    public class ParcelMachineController : ControllerBase
    {
        private readonly HttpClient _httpClient;

        public ParcelMachineController(HttpClient httpClient)
        {
            _httpClient = httpClient;
        }
        
        [HttpGet]
        public async Task<IActionResult> GetParcelMachines()
        {
            var response = await _httpClient.GetAsync("https://www.omniva.ee/locations.json");
            var responseBody = await response.Content.ReadAsStringAsync();
            return Content(responseBody, "application/json");
        }
    }
}

4.1.2. Lisame http-kliendi faili Program.cs:

C#
var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

builder.Services.AddHttpClient();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();

app.UseCors(options => options
    .WithOrigins("http://localhost:3000")
    .AllowAnyMethod()
    .AllowAnyHeader()
);

app.UseAuthorization();

app.MapControllers();

app.Run();

4.1.3. Uuenda App.js-i selle koodiga, et luua valikukast kõikide pakiautomaatidega:

JavaScript
import { useEffect, useState } from 'react';
import './App.css';

function App() {
  const [pakiautomaadid, setPakiautomaadid] = useState([]);

  useEffect(() => {
    fetch("https://localhost:4444/parcelmachine")
      .then(res => res.json())
      .then(json => setPakiautomaadid(json));
  }, []);

  return (
    <div className="App">
      <select>
        {pakiautomaadid.map(automaat => 
            <option>
                {automaat.NAME}
            </option>)}
      </select>
    </div>
  );
}

export default App;

4.2. Elering

4.2.1. Kleebi see kood:

NordpoolController.cs:

C#
using Microsoft.AspNetCore.Mvc;
using System.Text.Json;

namespace Veeb_TARpv23.Controllers
{
    [Route("[controller]")]
    [ApiController]
    public class NordpoolController : ControllerBase
    {

        private readonly HttpClient _httpClient;

        public NordpoolController(HttpClient httpClient)
        {
            _httpClient = httpClient;
        }

        [HttpGet("{country}/{start}/{end}")]
        public async Task<IActionResult> GetNordPoolPrices(
            string country,
            string start,
            string end)
        {
            var response = await _httpClient.GetAsync(
                $"https://dashboard.elering.ee/api/nps/price?start={start}&end={end}");
            var responseBody = await response.Content.ReadAsStringAsync();
            Console.WriteLine(responseBody);

            var jsonDoc = JsonDocument.Parse(responseBody);
            var dataProperty = jsonDoc.RootElement.GetProperty("data");

            string prices;

            switch (country)
            {
                case "ee":
                    prices = dataProperty.GetProperty("ee").ToString();
                    Console.WriteLine(responseBody);

                    return Content(prices, "application/json");

                case "lv":
                    prices = dataProperty.GetProperty("lv").ToString();
                    return Content(prices, "application/json");

                case "lt":
                    prices = dataProperty.GetProperty("lt").ToString();
                    return Content(prices, "application/json");

                case "fi":
                    prices = dataProperty.GetProperty("fi").ToString();
                    return Content(prices, "application/json");

                default:
                    return BadRequest("Invalid country code.");
            }
        }
    }
}

App.js:

JavaScript
import { useRef } from 'react';
import { useEffect, useState } from 'react';
import './App.css';

function App() {
  const [prices, setPrices] = useState([]);
  const [chosenCountry, setChosenCountry] = useState("ee");
  const [start, setStart] = useState("");
  const [end, setEnd] = useState("");
  const startRef = useRef();
  const endRef = useRef();

  useEffect(() => {
    if (start !== "" && end !== "") {
      fetch("https://localhost:4444/nordpool/" + chosenCountry + "/" + start + "/" + end)
        .then(res => res.json())
        .then(json => {
          setPrices(json);
        });
    }
  }, [chosenCountry, start, end]);

  function updateStart() {
    const startIso = new Date(startRef.current.value).toISOString();
    setStart(startIso);
  }

  function updateEnd() {
    const endIso = new Date(endRef.current.value).toISOString();
    setEnd(endIso);
  }

  return (
    <div>
      <button onClick={() => setChosenCountry("fi")}>Soome</button>
      <button onClick={() => setChosenCountry("ee")}>Eesti</button>
      <button onClick={() => setChosenCountry("lv")}>Läti</button>
      <button onClick={() => setChosenCountry("lt")}>Leedu</button>
      <input ref={startRef} onChange={updateStart} type="datetime-local" />
      <input ref={endRef} onChange={updateEnd} type="datetime-local" />
      {prices.length > 0 && 
      <table style={{marginLeft: "100px"}}>
        <thead>
          <th style={{border: "1px solid #ddd", padding: "12px", backgroundColor: "#04AA6D"}}>Ajatempel</th>
          <th style={{border: "1px solid #ddd", padding: "12px", backgroundColor: "#04AA6D"}}>Hind</th>
        </thead>
        <tbody>
          <td style={{position: "absolute", left: "30px"}}>{chosenCountry}</td>
          {prices.map(data => 
          <tr key={data.timestamp}>
            <td style={{border: "1px solid #ddd", padding: "8px"}}>{new Date(data.timestamp * 1000).toISOString()}</td>
            <td style={{border: "1px solid #ddd", padding: "8px"}}>{data.price}</td>
          </tr>)}
        </tbody>
      </table>}
    </div>
  );
}

export default App;

4.3. Payment controller

4.3.1. Kleebi see kood:

PaymentController.cs:

C#
using Microsoft.AspNetCore.Mvc;
using System.Net.Http.Headers;
using System.Text.Json;
using System.Text;

namespace Veeb_TARpv23.Controllers
{
    [Route("[controller]")]
    [ApiController]
    public class PaymentController : ControllerBase
    {
        private readonly HttpClient _httpClient;

        public PaymentController(HttpClient httpClient)
        {
            _httpClient = httpClient;
        }

        [HttpGet("{sum}")]
        public async Task<IActionResult> MakePayment(string sum)
        {
            var paymentData = new
            {
                api_username = "e36eb40f5ec87fa2",
                account_name = "EUR3D1",
                amount = sum,
                order_reference = Math.Ceiling(new Random().NextDouble() * 999999),
                nonce = $"a9b7f7e7as{DateTime.Now}{new Random().NextDouble() * 999999}",
                timestamp = DateTime.Now,
                customer_url = "https://maksmine.web.app/makse"
            };

            var json = JsonSerializer.Serialize(paymentData);
            var content = new StringContent(json, Encoding.UTF8, "application/json");

            var client = new HttpClient();
            client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", "ZTM2ZWI0MGY1ZWM4N2ZhMjo3YjkxYTNiOWUxYjc0NTI0YzJlOWZjMjgyZjhhYzhjZA==");

            var response = await client.PostAsync("https://igw-demo.every-pay.com/api/v4/payments/oneoff", content);

            if (response.IsSuccessStatusCode)
            {
                var responseContent = await response.Content.ReadAsStringAsync();
                var jsonDoc = JsonDocument.Parse(responseContent);
                var paymentLink = jsonDoc.RootElement.GetProperty("payment_link");
                return Ok(paymentLink);
            }
            else
            {
                return BadRequest("Payment failed.");
            }
        }
    }
}

4.3.2. Mine Swagger UI-sse ja testi seda.

See peaks andma sulle lingi.