Doubt with NFT transfer

Hi, I’ve been progressing with my first contract, which I’ve been testing through the Truffle console.
It is a fixed price marketplace type NFT, where the bidder’s money is retained in the contract, the owner keeps an offer for 24 hours, and can accept or reject. If he accepts, a commission is deducted for the platform and the money is sent to the owner. If he rejects, the funds are returned to the bidder.
Now, I have a conceptual error in the claim() on the part of the owner.
I have doubts about how to use the transfer() and the safeTransferFrom().
I need the money that I retained in the bidder’s contract, to be able to take out the commission and make the 2 transfers.
One transfer to the platform commission account, and another transfer to the owner’s account.
And of course, transfer the NFT. (How do I do this?)

I leave my code:

// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;

import "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721BurnableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/cryptography/ECDSAUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/math/SafeMathUpgradeable.sol";

contract Kargain is ERC721BurnableUpgradeable, AccessControlUpgradeable {
    using SafeMathUpgradeable for uint256;
    using ECDSAUpgradeable for bytes32;

    uint256 private constant COMMISSION_EXPONENT = 4;
    address payable private _platformAddress;
    uint256 private _platformCommissionPercent;

    mapping (uint => Token) private _tokens;
    mapping (uint256 => address payable) private _offer_vendor;
    mapping (uint256 => address payable) private _offers;
    mapping (uint256 => bool) private _token_exist;
    mapping (uint256 => bool) private _offer_claimed;
    mapping (uint256 => uint256) private _offers_closeTimestamp;

    event TokenMinted(address indexed creator, uint256 indexed tokenId);
    event OfferReceived(address indexed payer, uint tokenId);
    event OfferAccepted(address indexed payer, uint tokenId);
    event OfferRejected(address indexed payer, uint tokenId);

    struct Token {
        address owner;
        uint amount;
    }

    struct TransferType {
        address from;
        address to;
        uint256 tokenId;
        uint256 amount;
    }

    modifier onlyAdmin(){
        require(hasRole(DEFAULT_ADMIN_ROLE, _msgSender()), "Kargain: Caller is not a admin");
        _;
    }

    function initialize(address payable platformAddress_, uint256 platformCommissionPercent_)
    initializer public {
        _platformAddress = platformAddress_;
        _platformCommissionPercent = platformCommissionPercent_;
        __ERC721Burnable_init();
        __ERC721_init("Kargain", "KGN");
        __AccessControl_init();
        _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);
    }

    function platformCommissionPercent() public view returns(uint256){
        return _platformCommissionPercent;
    }

    function setPlatformCommissionPercent(uint256 _platformCommissionPercent_) public onlyAdmin {
        _platformCommissionPercent = _platformCommissionPercent_;
    }

    function platformAddress() public view returns(address payable){
        return _platformAddress;
    }

    function setPlatformAddress(address payable platformAddress_) public onlyAdmin {
        _platformAddress = platformAddress_;
    }

    function ownerOf(uint256 tokenId) public view override returns (address) {
        address owner = _tokens[tokenId].owner;
        require(owner != address(0), "ERC721: owner query for nonexistent token");
        return owner;
    }

    function mint(uint256 tokenId) public payable{
        require(!_token_exist[tokenId], "Kargain: Id for this token already exist");
        super._mint(msg.sender, tokenId);
        _tokens[tokenId].owner = msg.sender;
        _offers[tokenId] = payable(address(0));
        _offer_vendor[tokenId] = msg.sender;
        _token_exist[tokenId] = true;
        _offer_claimed[tokenId] = false;
        _tokens[tokenId].amount = msg.value;
        emit TokenMinted(msg.sender, tokenId);
    }

    function purchaseToken(uint256 _tokenId) public payable {
        require(_token_exist[_tokenId], "Kargain: Id for this token not exist");
        require(!_offer_claimed[_tokenId], "Kargain: Offer was claimed");
        require(_tokens[_tokenId].owner != msg.sender, "Kargain: You cannot buy your own token");
        require(_offers[_tokenId] == address(0), "Kargain: An offer is pending");
        require(msg.value == _tokens[_tokenId].amount, "Kargain: the offer amount is invalid");
        _offers_closeTimestamp[_tokenId] = block.timestamp;
        uint256 refundAmount = _tokens[_tokenId].amount;
        _offers[_tokenId] = payable(address(msg.sender));
        address payable refundAddress = _offers[_tokenId];
        refundAddress.transfer(refundAmount);
        emit OfferReceived(msg.sender, _tokenId);
    }

    function claimOffer(uint256 tokenId) public {
        require(_token_exist[tokenId], "Kargain: Id for this token not exist");
        require(!_offer_claimed[tokenId], "Kargain: Offer was claimed");
        require(_offers_closeTimestamp[tokenId] < block.timestamp, "Kargain: Offer has expired");
        _offer_claimed[tokenId] = true;
        uint256 platformCommission;
        platformCommission = (_tokens[tokenId].amount).mul(_platformCommissionPercent).div(10** COMMISSION_EXPONENT);
        safeTransferFrom(_offer_vendor[tokenId], , tokenId);
        _platformAddress.transfer(platformCommission);
        _offer_vendor[tokenId].transfer((_tokens[tokenId].amount).sub(platformCommission));
        _offer_vendor[tokenId] = _offers[tokenId];
    }

}

I get this error with safeTransferFrom(…)

{ Error: Returned error: VM Exception while processing transaction: revert ERC721: transfer of token that is not own – Reason given: ERC721: transfer of token that is not own.

thank you

The NFT owner should either transfer the NFT to the marketplace contract, or approve the marketplace to transfer it on their behalf.

For the second option you should look into the IERC721.approve function. This should make it so that safeTransferfFrom doesn’t revert.